Why are there extra characters in my char[]? - arrays

I,m trying to shorten a char[] by a specified number, and for some reason, I've got more characters in my new char[]. Can you help me fix this?
When I tried with 1 or 2 letters, the result is this:
(the d, n, k, a are the first letters of each lines reversed)
#▬w #▬n #▬k #▬a
(the di, an, ok, la are the first two letters of each lines reversed)
#id #an #ok #la
With 3 letters, it works perfectly:
nid ran rok mla
But same problem with more than 3:
qp░nnid qp░aran qp░trok qp░amla
And with more letters than the longest line, it also works perfectly:
eynnid scnaran etrok amla
<--- These are my words backwards --->
char **read(FILE *file, int lineLength, int *pLines)
{
size_t total = 0;
size_t allocated = START;
int sor = 0;
char buffer[MAX_LENGTH];
char shortened[lineLength];
/////////
//printf("%d", sizeof(shortened));
char **lines= (char **)malloc(allocated* sizeof(char *));
while (fgets(buffer, MAX_LENGTH, file) != NULL)
{
for (int i = 0; i < lineLength; i++)
{
shortened[i] = buffer[i];
}
int length = strlen(shortened);
if (shortened[length - 1] == '\n')
{
shortened[length - 1] = '\0';
}
if (line == allocated)
{
allocated*= 2;
lines= realloc(sorok, allocated* sizeof(char *));
}
lines[line] = (char *)malloc(lineLength);
strcpy(lines[line], shortened);
line++;
}
*pLines = line;
return lines;
}

One major problem is this loop:
for (int i = 0; i < lineLength; i++)
{
shortened[i] = buffer[i];
}
If lineLength > strlen(buffer) then you will copy the null-terminator (and beyond, including data that isn't initialized by the fgets call).
But if strlen(buffer) >= lineLength you will not copy the null-terminator. Then you use the strlen function on shortened which will then go beyond the end of shortened and you will have undefined behavior.
And for a better way to remove the newline (which you need to do for buffer and not shortened) see Removing trailing newline character from fgets() input

Related

padding string by adding char to the string

I am trying to create a padding function that adds an underscore to a string. The string length should be 16, and if the string is less than 16 the function should add an underscore until the string length is 16, then return the padding string. If the string is more than 16, the padding function should ignore the characters and returned the first 16 characters of the string.
char* padding(char* plaintext) {
char ch = '_';
size_t len = strlen(plaintext);
size_t lench = 17 - len;
char *p_text = malloc(len + 1 + 1);
strcpy(p_text, plaintext);
if (len < 17) {
int i;
for (i = lench; i < 16; i++) {
p_text[i] = ch;
}
}
// p_text[17] = '\0';
return p_text;
}
int main() {
char *key = "0123456789ABCDEF";
while (1) {
char plaintext[WIDTH + 1];
printf("Enter a string: ");
fgets(plaintext, WIDTH + 1, stdin);
if (plaintext[0] == '\n' || plaintext[0] == '\r')
break;
char* padded_plaintext = padding(plaintext);
printf("padded plaintext = %s\n", padded_plaintext);
printf("\n");
}
return 0;
}
this code returns a weird result.
Consider a clean solution to this problem. Hopefully seeing this (and the accompanying explanation) helps.
char *padded_string(char *src, int width, char ch) {
char *dest = calloc(1, width + 1);
strncpy(dest, src, width);
for (int i = 0; i < width; i++) {
if (!dest[i]) {
dest[i] = ch;
}
}
return dest;
}
We provide ourselves a clean slate to work on by allocating width + 1 bytes using calloc. Using calloc will ensure all bytes are set to 0. When working with strings in C, they need to be null-terminated, so this is very useful.
We copy the contents of src into dest. Using strncpy ensures we don't get a buffer overflow if the source string is longer than the string we want to end up with.
Next we loop from 0, width times. If the character in the destination string at i is a '\0' we'll insert the padding character at that index.
Now, because we used calloc and we allocated an extra byte beyond the character length we needed, the string is already null-terminated, we can simply return its pointer.
The space that you allocate for p_text should not depend on the input. It should always be 17. If len + 1 + 1 < 16, then accessing p_text[i] will lead to undefined behavior for certain values of i that you are using. You should replace:
char *p_text = malloc(len + 1 + 1);
with
char *p_text = malloc(17);
and check that p_text is not NULL before you write to it.
Also, the commented out //p_text[17] = '\0'; is wrong. That should be
p_text[16] = '\0';

How to Replace Leading or Trailing Blank Characters with "X"

Looking for a more efficient way to replace leading and trailing empty spaces (' ') and appending an 'X' to the front for each empty space.. It seems to work ok for trailing spaces but I'd like to know if there's a better / simpler way of going about this that I am missing.
Example:
Passed in string: '12345 '
Desired result 'XXXXX12345'
Removed 5 empty spaces and append 5 'X's to front.
Example:
Passed in string: ' 12345'
Desired result 'XX12345'
Remove 2 empty spaces and append 2 'X's to front.
void fixStr(char* str)
{
int i = 0;
int length = strlen(str);
char strCopy[10];
strcpy(strCpy, str);
for(i = 0; i < length; i++)
{
if(strCopy[i] == ' ')
{
strCopy[i] = '\0';
str[i] = '\0';
break;
}
}
for(i = 0; i < length - i + 2; i++)
{
str[i] = 'X';
str[i + 1] = '\0';
}
strcat(str, strCopy);
}
One way to achieve this is to find out the leading non-space position & trailing non-space position of the string, and then move the content in-between (leading nonspace, trailing nonspace) this to end of the string, then set all the empty space at the beginning to 'x'
This way you can get the expected output (function below)
void fixStr(char* str)
{
int i = 0;
int length = strlen(str);
int leadindex = length;
int tailindex = 0;
// First find the leading nonspace position
for(i = 0; i < length; i++)
{
if(str[i] != ' ')
{
leadindex = i;
break;
}
}
// if not found nonspace then no change
if( leadindex == length )
{
// all spaces, so no change required;
return;
}
// Find the trailing nonspace position
for(i = length - 1; i >= 0 ; i--)
{
if(str[i] != ' ')
{
tailindex = i;
break;
}
}
// move the buffer (in place) to exclude trailing spaces
memmove(str + (length - tailindex -1),str,(tailindex +1) );
// set the 'x' to all empty spaces at leading ( you may use for loop to set this)
memset(str, 'X', length - (tailindex - leadindex + 1) );
}
To solve a problem the engineer's way:
Define the needs.
Know your tools.
Use the tools as simple as possible, as accurate as necessary to make up a solution.
In your case:
Needs:
find the number of trailing spaces
move content of string to the end
set beginning to 'X's
Tools:
to measure, iterate, compare and count
to move a block of memory
to initialise a block of memory
Example for a solution:
#include <string.h> /* for strlen(), memmove () and memset() */
void fix_str(char * s)
{
if ((NULL != s) && ('\0' != *s)) /* Ignore NULL and empty string! */
{
/* Store length and initialise counter: */
size_t l = strlen(s), i = l;
/* Count space(s): */
for (; (0 != i) && (' ' == s[i-1]); --i); /* This for loop does not need a "body". */
/* Calculate the complement: */
size_t c = l - i;
/* Move content to the end overwriting any trailing space(s) counted before hand: */
memmove(s+c, s, i); /* Note that using memmove() instead of memmcpy() is essential
here as the source and destination memory overlap! */
/* Initialise the new "free" characters at the beginning to 'X's:*/
memset(s, 'X', c);
}
}
I didn't fix your code but you could use sprintf in combination with isspace, something along the lines of this. Also, remember to make a space for the '\0 at the end of your string. Use this idea and it should help you:
#include <ctype.h>
#include <stdio.h>
int main()
{
char buf[11];
char *s = "Hello";
int i;
sprintf(buf, "%10s", s); /* right justifies in a column of 10 in buf */
for(i = 0; i < 10; i++) {
if(isspace(buf[i])) /* replace the spaces with an x (or whatever) */
buf[i] = 'x';
}
printf("%s\n", buf);
return 0;
}

Odd behavior removing duplicate characters in a C string

I am using the following method in a program used for simple substitution-based encryption. This method is specifically used for removing duplicate characters in the encryption/decryption key.
The method is functional, as is the rest of the program, and it works for 99% of the keys I've tried. However, when I pass it the key "goodmorning" or any key consisting of the same letters in any order (e.g. "dggimnnooor"), it fails. Further, keys containing more characters than "goodmorning" work, as well as keys with less characters.
I ran the executable through lldb with the same arguments and it works. I've cloned my repository on a machine running CentOS, and it works as is.
But I get no warnings or errors on compile.
//setting the key in main method
char * key;
key = removeDuplicates(argv[2]);
//return 1 if char in word
int targetFound(char * charArr, int num, char target){
int found = 0;
if(strchr(charArr,target))
found = 1;
return found;
}
//remove duplicate chars
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len * sizeof(char));
if (result == NULL)
errorHandler(2);
char ch;
int i;
int j;
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
}
}
return result;
}
Per request: if "feather" was passed in to this function the resulting string would be "feathr".
As R Sahu already said, you are not terminating your string with a NUL character. Now I'm not going to explain why you need to do this, but you always need to terminate your strings with a NUL character, which is '\0'. If you want to know why, head over here for a good explanation. However this is not the only problem with your code.
The main problem is that the function strchr that you are calling to find out if your result already contains some character expects you to pass a NUL terminated string, but your variable is not NUL terminated, because you keep appending characters to it.
To solve your problem, I would suggest you to use a map instead. Map all the characters you already used and if they aren't in the map add them both to the map and the result. This is simpler (no need to call strchr or any other function), faster (no need to scan all the string every time), and most importantly correct.
Here's a simple solution:
char *removeDuplicates(char *word){
char *result, *map, ch;
int i, j;
map = calloc(256, 1);
if (map == NULL)
// Maybe you want some other number here?
errorHandler(2);
// Add one char for the NUL terminator:
result = malloc(strlen(word) + 1);
if (result == NULL)
errorHandler(2);
for(i = 0, j = 0; word[i] != '\0'; i++) {
ch = word[i];
// Check if you already saw this character:
if(map[(size_t)ch] == 0) {
// If not, add it to the map:
map[(size_t)ch] = 1;
// And to your result string:
result[j] = ch;
j++;
}
}
// Correctly NUL terminate the new string;
result[j] = '\0';
return result;
}
Why does this work on other machines, but not on your machine?
You are being a victim of undefined behavior. Different compilers on different systems treat undefined behavior differently. For example, GCC may decide to not do anything in this particular case and make strchr just keep searching in the memory until it founds a '\0' character, and this is exactly what happens. Your program keeps searching for the NUL terminator and never stops because who knows where a '\0' could be in memory after your string? This is both dangerous and incorrect, because the program is not reading inside the memory reserved for it, so for example, another compiler could decide to stop the search there, and give you a correct result. This however is not something to take for granted, and you should always avoid undefined behavior.
I see couple of problems in your code:
You are not terminating the output with the null character.
You are not allocating enough memory to hold the null character when there are no duplicate characters in the input.
As a consequence, your program has undefined behavior.
Change
result = malloc (len * sizeof(char));
to
result = malloc (len+1); // No need for sizeof(char)
Add the following before the function returns.
result[j] = '\0';
The other problem, the main one, is that you are using strchr on result, which is not a null terminated string when you call targetFound. That also caused undefined behavior. You need to use:
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len+1);
if (result == NULL)
{
errorHandler(2);
}
char ch;
int i;
int j;
// Make result an empty string.
result[0] = '\0';
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
// Null terminate again so that next call to targetFound()
// will work.
result[j] = '\0';
}
}
return result;
}
A second option is to not use strchr in targetFound. Use num instead and implement the equivalent functionality.
int targetFound(char * charArr, int num, char target)
{
for ( int i = 0; i < num; ++i )
{
if ( charArr[i] == target )
{
return 1;
}
}
return 0;
}
That will allow you to avoid assigning the null character to result so many times. You will need to null terminate result only at the end.
char * removeDuplicates(char * word){
char * result;
int len = strlen(word);
result = malloc (len+1);
if (result == NULL)
{
errorHandler(2);
}
char ch;
int i;
int j;
for( i = 0, j = 0; i < len; i++){
ch = word[i];
if(!targetFound(result, i, ch)){
result[j] = ch;
j++;
}
}
result[j] = '\0';
return result;
}

Returning the length of a char array in C

I am new to programming in C and am trying to write a simple function that will normalize a char array. At the end i want to return the length of the new char array. I am coming from java so I apologize if I'm making mistakes that seem simple. I have the following code:
/* The normalize procedure normalizes a character array of size len
according to the following rules:
1) turn all upper case letters into lower case ones
2) turn any white-space character into a space character and,
shrink any n>1 consecutive whitespace characters to exactly 1 whitespace
When the procedure returns, the character array buf contains the newly
normalized string and the return value is the new length of the normalized string.
*/
int
normalize(unsigned char *buf, /* The character array contains the string to be normalized*/
int len /* the size of the original character array */)
{
/* use a for loop to cycle through each character and the built in c functions to analyze it */
int i;
if(isspace(buf[0])){
buf[0] = "";
}
if(isspace(buf[len-1])){
buf[len-1] = "";
}
for(i = 0;i < len;i++){
if(isupper(buf[i])) {
buf[i]=tolower(buf[i]);
}
if(isspace(buf[i])) {
buf[i]=" ";
}
if(isspace(buf[i]) && isspace(buf[i+1])){
buf[i]="";
}
}
return strlen(*buf);
}
How can I return the length of the char array at the end? Also does my procedure properly do what I want it to?
EDIT: I have made some corrections to my program based on the comments. Is it correct now?
/* The normalize procedure normalizes a character array of size len
according to the following rules:
1) turn all upper case letters into lower case ones
2) turn any white-space character into a space character and,
shrink any n>1 consecutive whitespace characters to exactly 1 whitespace
When the procedure returns, the character array buf contains the newly
normalized string and the return value is the new length of the normalized string.
*/
int
normalize(unsigned char *buf, /* The character array contains the string to be normalized*/
int len /* the size of the original character array */)
{
/* use a for loop to cycle through each character and the built in c funstions to analyze it */
int i = 0;
int j = 0;
if(isspace(buf[0])){
//buf[0] = "";
i++;
}
if(isspace(buf[len-1])){
//buf[len-1] = "";
i++;
}
for(i;i < len;i++){
if(isupper(buf[i])) {
buf[j]=tolower(buf[i]);
j++;
}
if(isspace(buf[i])) {
buf[j]=' ';
j++;
}
if(isspace(buf[i]) && isspace(buf[i+1])){
//buf[i]="";
i++;
}
}
return strlen(buf);
}
The canonical way of doing something like this is to use two indices, one for reading, and one for writing. Like this:
int normalizeString(char* buf, int len) {
int readPosition, writePosition;
bool hadWhitespace = false;
for(readPosition = writePosition = 0; readPosition < len; readPosition++) {
if(isspace(buf[readPosition]) {
if(!hadWhitespace) buf[writePosition++] = ' ';
hadWhitespace = true;
} else if(...) {
...
}
}
return writePosition;
}
Warning: This handles the string according to the given length only. While using a buffer + length has the advantage of being able to handle any data, this is not the way C strings work. C-strings are terminated by a null byte at their end, and it is your job to ensure that the null byte is at the right position. The code you gave does not handle the null byte, nor does the buffer + length version I gave above. A correct C implementation of such a normalization function would look like this:
int normalizeString(char* string) { //No length is passed, it is implicit in the null byte.
char* in = string, *out = string;
bool hadWhitespace = false;
for(; *in; in++) { //loop until the zero byte is encountered
if(isspace(*in) {
if(!hadWhitespace) *out++ = ' ';
hadWhitespace = true;
} else if(...) {
...
}
}
*out = 0; //add a new zero byte
return out - string; //use pointer arithmetic to retrieve the new length
}
In this code I replaced the indices by pointers simply because it was convenient to do so. This is simply a matter of style preference, I could have written the same thing with explicit indices. (And my style preference is not for pointer iterations, but for concise code.)
if(isspace(buf[i])) {
buf[i]=" ";
}
This should be buf[i] = ' ', not buf[i] = " ". You can't assign a string to a character.
if(isspace(buf[i]) && isspace(buf[i+1])){
buf[i]="";
}
This has two problems. One is that you're not checking whether i < len - 1, so buf[i + 1] could be off the end of the string. The other is that buf[i] = "" won't do what you want at all. To remove a character from a string, you need to use memmove to move the remaining contents of the string to the left.
return strlen(*buf);
This would be return strlen(buf). *buf is a character, not a string.
The notations like:
buf[i]=" ";
buf[i]="";
do not do what you think/expect. You will probably need to create two indexes to step through the array — one for the current read position and one for the current write position, initially both zero. When you want to delete a character, you don't increment the write position.
Warning: untested code.
int i, j;
for (i = 0, j = 0; i < len; i++)
{
if (isupper(buf[i]))
buf[j++] = tolower(buf[i]);
else if (isspace(buf[i])
{
buf[j++] = ' ';
while (i+1 < len && isspace(buf[i+1]))
i++;
}
else
buf[j++] = buf[i];
}
buf[j] = '\0'; // Null terminate
You replace the arbitrary white space with a plain space using:
buf[i] = ' ';
You return:
return strlen(buf);
or, with the code above:
return j;
Several mistakes in your code:
You cannot assign buf[i] with a string, such as "" or " ", because the type of buf[i] is char and the type of a string is char*.
You are reading from buf and writing into buf using index i. This poses a problem, as you want to eliminate consecutive white-spaces. So you should use one index for reading and another index for writing.
In C/C++, a native string is an array of characters that ends with 0. So in essence, you can simply iterate buf until you read 0 (you don't need to use the len variable at all). In addition, since you are "truncating" the input string, you should set the new last character to 0.
Here is one optional solution for the problem at hand:
int normalize(char* buf)
{
char c;
int i = 0;
int j = 0;
while (buf[i] != 0)
{
c = buf[i++];
if (isspace(c))
{
j++;
while (isspace(c))
c = buf[i++];
}
if (isupper(c))
buf[j] = tolower(c);
j++;
}
buf[j] = 0;
return j;
}
you should write:
return strlen(buf)
instead of:
return strlen(*buf)
The reason:
buf is of type char* - it's an address of a char somewhere in the memory (the one in the beginning of the string). The string is null terminated (or at least should be), and therefore the function strlen knows when to stop counting chars.
*buf will de-reference the pointer, resulting on a char - not what strlen expects.
Not much different then others but assumes this is an array of unsigned char and not a C string.
tolower() does not itself need the isupper() test.
int normalize(unsigned char *buf, int len) {
int i = 0;
int j = 0;
int previous_is_space = 0;
while (i < len) {
if (isspace(buf[i])) {
if (!previous_is_space) {
buf[j++] = ' ';
}
previous_is_space = 1;
} else {
buf[j++] = tolower(buf[i]);
previous_is_space = 0;
}
i++;
}
return j;
}
#OP:
Per the posted code it implies leading and trailing spaces should either be shrunk to 1 char or eliminate all leading and trailing spaces.
The above answer simple shrinks leading and trailing spaces to 1 ' '.
To eliminate trailing and leading spaces:
int i = 0;
int j = 0;
while (len > 0 && isspace(buf[len-1])) len--;
while (i < len && isspace(buf[i])) i++;
int previous_is_space = 0;
while (i < len) { ...

Array of separators for strtok() function

I want to divide my text into words. Separator is any symbol except latin letters.
Here i have loop, filling my separators array:
for(i = 0; i <= 127; i ++) {
if(!isalpha(i)) {
separators = (char*) realloc(separators, (length + 1) * sizeof(char));
separators[length] = i;
length ++;
}
}
Then i use it here:
char text[] = "hello world!";
char** words = NULL;
char* p = strtok(text, separators);
int cnt = 0;
while(p != NULL) {
words = (char**) realloc(words, (cnt + 1) * sizeof(char*));
words[cnt] = strdup(p);
cnt ++;
p = strtok(NULL, separators);
}
for(i = 0; i < pnt; i ++) {
printf(" - %d %s\n", i + 1, words[i]);
}
As a result a have:
-1 hello world!
If separators array is replaced by " " is works well.
What's the problem with array?
The first value of i in your loop, 0, is not alpha; so a 0 will be stored as the very first byte in the separator array.
strtok() expects to receive the separator list as a string, and strings in C are terminated by a zero. So strtok() receives a sequence beginning with a terminator, and it thinks is an empty list, with no separators at all.
You can start the array from 1 to get rid of that interfering zero:
for (i = 1; i <= 127; i ++) {
if(!isalpha(i)) {
separators = (char*) realloc(separators, (length + 1) * sizeof(char));
separators[length] = i;
length ++;
}
}
// then you also need to terminate it, otherwise strtok() will continue reading
// past the end of the array, with unpredictable (but very likely undesirable) results.
separators[length] = 0x0;
You might also want instead to allocate the string only once (you waste some space, but save some time);
#define MAX_SEPARATORS 128
separators = (char*) malloc(separators, MAX_SEPARATORS * sizeof(char));
for (i = 1; i < MAX_SEPARATORS; i++) {
if (!isalpha(i)) {
separators[length++] = i;
}
}
separators[length] = 0x0;
You have to remember that the strtok wants the separators as a string, complete with a string terminator character ('\0'). Unfortunately you don't have that terminator in the separators "string", so strtok will read that one beyond what you have allocated leading to undefined behavior.

Resources