ThomasWest
ThomasWest

Reputation: 505

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?

Upvotes: 0

Views: 82

Answers (4)

Jonathan Leffler
Jonathan Leffler

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:

  • I've passed the file names to the function, rather than hard-coding them. This also makes it easier to report errors; you can identify the file.
  • Errors should be reported on the standard error channel, of course.
  • Personally, I find the (common) asymmetry between 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.
  • This code checks that both files are opened successfully; your code assumed that the second file could be opened each time and would (probably) crash if it suddenly couldn't be opened.
  • I'm assuming a C99 compiler so variables can be defined when needed; if you're stuck with a C90 compiler, move the declarations to the top and change my initializations into assignments.
  • I use the buf[strcspn(buf, "\n")] = '\0'; notation to safely zap the first (and only) newline in the buffer.
  • I then calculate the length of the line once.
  • If the line is shorter than 10, then I use 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.

  • Both files are explicitly closed.
  • Paranoid code would check that the output file was closed successfully and report problems if there are any.
  • You could decide whether to report errors on the input file too — you might use ferror() to detect errors on the input.
  • In other circumstances, I might use POSIX's 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

pkthapa
pkthapa

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

LambdaBeta
LambdaBeta

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

Jim B.
Jim B.

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

Related Questions