ThomasWest ThomasWest - 17 days ago 9
C Question

Add spaces to words in textfile

I have a simple textfile containing:

dog
bear
panda
fish
elephant


However, I want all of them to have a full 10 characters.

Thus I want to fill the rest of the word with 'x' like:

dogxxxxxxx
bearxxxxxx
pandaxxxxx
fishxxxxxx
elephantxx


What I have so far is:

int filterWords(){
FILE *ptr_file;
FILE *ptr_file2;
char buf[1000];
int i = 0;
int numberSpaces;

ptr_file =fopen("animals.txt","r");
if (!ptr_file)
return 1;

while (fgets(buf,1000, ptr_file)!=NULL){
if(strlen(buf) < 10){
numberSpaces = 10 - strlen(buf)-1;
memset(buf, 'x', numberSpaces);
buf[strlen(buf)+numberSpaces] = '\0';
ptr_file2 = fopen("newAnimal.txt", "a");
fputs(buf, ptr_file2);
fclose(ptr_file2);
}
}

fclose(ptr_file);
return 0;
}


However, when I tried to open the
newAnimal.txt
, I got something like this:

dog
xxxbxxxear
pandaxxxf
ishxxxxxx
....


All are mislocated. What is wrong with my code?

Answer

Your issue is a number of off-by-one and similar logic errors conspiring against you. Let us take a look at that if statement in your main loop and see what's wrong:

if(strlen(buf) < 10){
    numberSpaces = 10 - strlen(buf)-1;
    memset(buf, 'x', numberSpaces);
    buf[strlen(buf)+numberSpaces] = '\0';
    ptr_file2 = fopen("newAnimal.txt", "a");
    fputs(buf, ptr_file2);
    fclose(ptr_file2);
}

First remember that fgets will store the newline, so buf will have an extra newline on it. This will increase is length (from strlen) by 1. So we have to adjust:

if (strlen(buf) < 11) {
    numberSpaces = 10 - (strlen(buf)-1); // otherwise - is done from the total, not from strlen
    memset(buf, 'x', numberSpaces); // still wrong...
    buf[strlen(buf)+numberSpaces] = '\0'; // still wrong...
    // fopen/fclose EVERY loop is terribly inefficient, but not wrong per se
    ptr_file2 = fopen("newAnimal.txt", "a");
    fputs(buf, ptr_file2);
    fclose(ptr_file2);
}

Now we need to fix our memset bufset issues because we don't want to memset the start of the string with 'x', but rather the end. Similarly we don't want to end with a '\0' we want '\n\0'. We also want to toss that immediately at the end, not after an additional numberSpaces wait...

if (strlen(buf) < 11) {
    numberSpaces = 10 - (strlen(buf)-1); 
    memset(buf + (strlen(buf)-1), 'x', numberSpaces); // now it will start from the correct spot
    buf[10] = '\n'; // we know how long we want our string to be ;)
    buf[11] = '\0';
    // fopen/fputs/fclose here (or outside the loop if you can)
}

One last thing to note: you should make sure your buffer is filled with 0 before you start using it... just turn the line char buf[1000]; into char buf[1000] = {0}; to be safe.

Another, more elegant, solution involves using strcspn to remove the newline only when applicable via buf[strcspn(buf, "\n")] = '\0';. strcspn is not a well-known function, but it searches for the character(s) provided and returns the index of the first occurance. It also takes the '\0' into account, so if it finds nothing it will return the length of the string. After correcting for the newline you don't need to subtract 1 from all the strlen calls anymore.