简体   繁体   中英

Using command line arguments to find and replace a word in a file in C

I have a txt file with some text in it. All I have to do is to find a given word in that file and change it with a new given word. These words and text file name will be supplied at the command prompt with the format C> findword <old word> <new word> <filename> where findword is executable file. A word will end either at a '.' , a ' ' or at a '\\n' . After a word ends if we have some extra space or newline, we will simply ignore it. I have written the below code for this and opened the file in r+ mode. I don't want to use a temporary file here. All updates should be done on same file.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main(int argc, char *argv[])
{
    if(argc != 4)
    {
        printf("Improper number of arguments\n");
        exit(1);
    }

    FILE *fp = fopen(argv[3], "r+");
    if(fp == NULL)
    {
        printf("Unable to open file\n");
        exit(2);
    }

    char word[25];
    int i, flag = 0, len, k;
    char ch;
    len = strlen(argv[1])+1;//assuming new and old words are of same length
    for(i=0;1;i++)
    {
        word[i] = fgetc(fp);
        if(word[i] == EOF)
        {
            break;
        }

        if((word[i] == ' ' || word[i] == '.' || word[i] == '\n') && (flag == 0))
        {
            word[i] = '\0';
            if(strcmp(argv[1], word) == 0)
            {
                fseek(fp, -(len), SEEK_CUR);//To seek back to the start of the old word
                k = fputs(argv[2], fp);//writing new word n place of old word
            }
            flag = 1;//whenever a word is formed, flag is set to 1

        }
        //Below code is used to check if next charcter is whitespace or alphabet, so that we can start forming a new word
        ch = fgetc(fp);
        if(ch == EOF)
        {
            break;
        }
        if((flag == 1) && (ch != ' ' && ch != '\n'))//if ch is whitespace or newline we have to simply ignore it.
        {
            i = -1;//i will get incremented to 0 in next iteration and we can start forming new word
            flag = 0;//signifies we will be forming a new word from next iteration.
        }
        fseek(fp, -1, SEEK_CUR);
    }
    fclose(fp);
    return 0;
}

Now suppose the text file had a single line written as This is business. . When i write command on cmd as C> findword is zz test.txt where is is the old word which has to be replaced, zz is the new word and test.txt is the text file. After this command executes i was expecting the text file to be updated to This zz business but to my surprise the text file was rewritten as This zzubusiness. . The issue is after updating the word is , the space after is is also getting updated. In this case it is replaced with character u . I tried this code with different words and text files and found that the space after is is always getting replaced with the second character of the immediate next word. For ex: here space is getting replaced with u which is second character of the word business . Now i have already spent a lot of time debugging this code but failed to do so. I know this code is not very efficient but I have intentionally written it just to do some experiments with it. What is the issue with this code ? Is it related to using both fgetc() and fputs() on the same file? or something else that i am missing ?

from man fopen (emphasis mine)

Reads and writes may be intermixed on read/write streams in any order. Note that ANSI C requires that a file positioning function intervene between output and input, unless an input operation encounters end-of-file. (If this condition is not met, then a read is allowed to return the result of writes other than the most recent.) Therefore it is good practice (and indeed sometimes necessary under Linux) to put an fseek(3) or fgetpos(3) operation between write and read operations on such a stream. This operation may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its synchronizing side effect).

That "indeed sometimes necessary" is what you've experienced and why fseek(fp,0,SEEK_CUR); or fflush(fp); after fputs() solve the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM