Reputation: 505
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?
Upvotes: 0
Views: 82
Reputation: 753675
And a third offering…take note of what's in here, but choose one of the others as the accepted answer.
#include <stdio.h>
#include <string.h>
static int filterWords(const char *ifile, const char *ofile)
{
FILE *ptr_file1 = fopen(ifile, "r");
if (!ptr_file1)
{
fprintf(stderr, "Failed to open file %s for reading\n", ifile);
return 1;
}
FILE *ptr_file2 = fopen(ofile, "w");
if (!ptr_file2)
{
fprintf(stderr, "Failed to open file %s for writing\n", ofile);
fclose(ptr_file1);
return 1;
}
char buf[1000];
while (fgets(buf, sizeof(buf), ptr_file1) != NULL)
{
buf[strcspn(buf, "\n")] = '\0';
int len = strlen(buf);
if (len < 10)
{
memset(buf + len, 'x', 10 - len);
buf[10] = '\0';
}
fprintf(ptr_file2, "%s\n", buf);
}
fclose(ptr_file1);
fclose(ptr_file2);
return 0;
}
int main(void)
{
printf("Status: %d\n", filterWords("animals.txt", "newAnimal.txt"));
return 0;
}
Notes:
ptr_file
and ptr_file2
annoying; I use ptr_file
for one, but then use ptr_file1
and ptr_file2
when I need the two names. Actually, for file pointers, I'd usually use either fp1
and fp2
or ifp
and ofp
, but I kept closer to the original code this time.buf[strcspn(buf, "\n")] = '\0';
notation to safely zap the first (and only) newline in the buffer.memset()
to set the trailing part to x
's and add a null byte.The thrice-repeated 10
should be a named constant, such as:
enum { PAD_LENGTH = 10 };
The code opens the secondary file once and writes all the names, even those not needing any padding, to the file.
ferror()
to detect errors on the input.getline()
to read the line. One big advantage is that it returns the number of characters in the line, so you don't have to search for the newline in the same way (though the last line of a file without a trailing newline needs to be worried about).Input:
dog
bear
panda
fish
elephant
alligator
rhinoceros
hippopotamus
East African Lion
Output:
dogxxxxxxx
bearxxxxxx
pandaxxxxx
fishxxxxxx
elephantxx
alligatorx
rhinoceros
hippopotamus
East African Lion
Upvotes: 2
Reputation: 1071
Below code will solve your issue:
int filterWords(void)
{
FILE *ptr_file;
FILE *ptr_file2;
char buf[1000];
int strLen = 0;
int numberSpaces;
ptr_file = fopen("animals.txt","r");
if (!ptr_file)
return 1;
while (fgets(buf, 1000, ptr_file)!=NULL)
{
strLen = strlen(buf) - 1;
if(strLen < 10){
numberSpaces = 10 - (strLen);
memset(&buf[strLen], 'x', numberSpaces);
buf[strLen + numberSpaces] = '\n';
buf[strLen + numberSpaces + 1] = '\0';
ptr_file2 = fopen("newAnimal.txt", "a");
fputs(buf, ptr_file2);
fclose(ptr_file2);
}
}
fclose(ptr_file);
return 0;
}
Upvotes: 0
Reputation: 1505
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.
Upvotes: 2
Reputation: 4694
For starters,
memset(buf, 'x', numberSpaces);
should be
memset(&buf[strlen(buf)], 'x', numberSpaces);
Also, you're calculating numberSpaces wrong. Instead of:
numberSpaces = 10 - strlen(buf)-1;
It should be:
numberSpaces = 10 - strlen(buf);
With that corrected, you need to put the \0 in the right place. Instead of:
buf[strlen(buf)+numberSpaces] = '\0';
It becomes:
buf[strlen(buf)] = '\0';
Your while loop now looks like:
while (fgets(buf,1000, ptr_file)!=NULL){
if(strlen(buf) < 10){
numberSpaces = 10 - strlen(buf);
memset(&buf[strlen(buf), 'x', numberSpaces);
buf[strlen(buf)] = '\0';
ptr_file2 = fopen("newAnimal.txt", "a");
fputs(buf, ptr_file2);
fclose(ptr_file2);
}
}
Upvotes: 2