Search code examples
cwhile-loopfile-handling

Why does this program fail to write the character in the file properly and instead get stuck in an infinite loop?


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

int main() {
    FILE *fp;
    char ch;

    fp = fopen("sample.txt", "r+");
    if (fp == NULL) {
        printf("Problem accessing the file!");
        exit(1);
    }

    while ((ch = fgetc(fp)) != EOF) {
        if (ch == ' ') {
            fseek(fp, -1l, SEEK_CUR);
            fputc('_', fp);
        }
    }

    fclose(fp);

    return 0;
}

In this program, I wanted the all the white-space characters in the opened file to be replaced with underscore characters. But this program is behaving in a very unexpected manner and getting stuck in an infinite loop. How do I make it work properly?

I tried to flush the stream fp on each iteration of the while loop but still it doesn't give the intended output.


Solution

  • ch must be defined with type int to accommodate all byte values read by fgetc() and the special negative value of the EOF macro.

    Using char makes it impossible to test EOF on your platform because type char seems to be unsigned, hence the value EOF is converted to a positive value 255 when stored into ch, which compares different from EOF in the test.

    Furthermore, you must issue a call to fseek() or rewind() before switching from reading from the stream to writing to it, which you do, but also to switch back to reading, which you don't.

    Finally, you should open the file as binary for fseek to operate correctly with absolute numbers such as -1.

    Here is a modified version:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
        FILE *fp;
        int ch;
    
        fp = fopen("sample.txt", "rw+");
        if (fp == NULL) {
            printf("Problem accessing the file!");
            exit(1);
        }
    
        while ((ch = fgetc(fp)) != EOF) {
            if (ch == ' ') {
                fseek(fp, -1l, SEEK_CUR);
                fputc('_', fp);
                fseek(fp, 0L, SEEK_CUR);
            }
        }
    
        fclose(fp);
    
        return 0;
    }