0
int main() {
    
    FILE* o = fopen("o.txt","r+");
    char c;
    while(!feof(o)){
        c = fgetc(o);
        if(c == 'h'){
            fputc('#',o);
        }else {
            fputc(c,o);
        }
    }
    
    return 0;
}

this is code, I am trying to edit text file, Whenever h letter appears I want to change it with # and other stays same. But when I run this code program does not. what am I doing wrong.

3 Answers3

1

When opening a file in "r+" update mode, you open the file for both reading and writing.

However, this does not mean that you can freely switch between reading and writing. According to §7.21.5.3 ¶7 of the ISO C11 standard, there are certain restrictions:

  • Output cannot be followed by input without an intervening call to fflush or to a file positioning function (fseek, fsetpos or rewind).
  • Input cannot be followed by output without an intervening call to a file positioning function, unless the input operation encountered end-of-file.

In your posted code, you are violating both rules, which has the consequence that your program is invoking undefined behavior. This means that anything may happen, including the possibility that your I/O functions are reading or writing nonsense.

Also, your program has another bug. The line

while(!feof(o)){

does not make sense. See the following question for the reason why:

Why is “while ( !feof (file) )” always wrong?

Also, you should always close the file with fclose, otherwise, depending on your platform, it is possible that the output buffer will not be flushed.

Here is a program which fixes all the bugs described above:

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

int main()
{ 
    FILE *fp;

    //attempt to open file
    fp = fopen( "o.txt", "r+" );
    if ( fp == NULL )
    {
        fprintf( stderr, "error opening file!\n" );
        exit( EXIT_FAILURE );
    }

    for (;;) //infinite loop, equivalent to while(1)
    {
        long prev_pos;
        int c;

        //remember previous file position, so that we can jump
        //back to it for writing, if necessary
        prev_pos = ftell( fp );

        //attempt to read next character
        c = fgetc( fp );

        //break infinite loop if end-of-file (or error) encountered
        if ( c == EOF )
            break;

        //replace character, if appropriate
        if ( c == 'h' )
        {
            //jump back to previous position
            fseek( fp, prev_pos, SEEK_SET );

            //write replacement character
            fputc( '#', fp );

            //flush the stream
            fflush( fp );
        }
    }

    //close the file
    fclose( fp );

    return 0;
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
1
  1. char c it has to be int c
  2. while(!feof(o)) - read Andreas Wenzel's comment
while((c = fgetc(o)) != EOF)

The read moves the file pointer one char ahead, so you need to move it back to write to the previous position.

for example

fseek(o, -1L, SEEK_CUR);
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 1
    According to [§7.21.9.2 ¶4 of the ISO C11 standard](http://port70.net/~nsz/c/c11/n1570.html#7.21.9.2p4), the line `fseek(o, -1L, SEEK_CUR);` will invoke undefined behavior. Since the file is opened in text mode, only `0` and the return value of a previous call to `ftell` are permissive values. However, I believe that in this case, it will probably work on most platforms, because the character `h` is unlikely to be translated in text mode. The main thing that gets translated is `\r\n` to `\n`. – Andreas Wenzel Jan 03 '22 at 20:51
0

In your post, your if statement had an else clause which would write to the file, but you don't want to do that. You've opened the file in r+ and the contents of the file will not be truncated. So to have the effect you intend, you should simply scan through each character, once you've read the character you're looking for which in this case is 'h', you will need to move the file pointer 1 character backwards. You can do that with fseek(o, -1L, SEEK_CUR);. Then perform your fputc() to overwrite that character in the file with the '#'

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

int main() {
    FILE* o = fopen("o.txt","r+");
    int c;
    while((c = fgetc(o)) != EOF) {
        if(c == 'h'){
            fseek(o, -1L, SEEK_CUR);
            fputc('#',o);
        }
    }

    return 0;
}

o.txt before: aaaahaaaa

o.txt after: aaaa#aaaa

codyne
  • 552
  • 1
  • 9
  • 1
    This answer would be improved by some elaboration on what was wrong with the OP's code, what you've changed, and how that answers the question. – Chris Jan 03 '22 at 19:57
  • 1
    Technically still UB due to not calling `fflush` after `fputc` and before the following `fgetc` though most implementation will do the right thing anyways. – Chris Dodd Jan 03 '22 at 20:08
  • The line `fseek(o, -1L, SEEK_CUR);` will not work reliably on all platforms. I suggest that you read [the documentation for that function](https://en.cppreference.com/w/c/io/fseek), especially this paragraph: `"If the stream is open in text mode, the only supported values for offset are zero (which works with any origin) and a value returned by an earlier call to ftell on a stream associated with the same file (which only works with origin of SEEK_SET)."` For example, on Microsoft Windows, `\r\n` is translated to `\n`, so that you may have to jump back two bytes instead of only one.[continued] – Andreas Wenzel Jan 03 '22 at 20:30
  • [continuation of previous comment] Although in this example, it will probably work on Microsoft Windows, because you are only calling `fseek` when the character is `h`. However, other platforms may perform other translations for text mode, so it is still dangerous to violate this rule. – Andreas Wenzel Jan 03 '22 at 20:36
  • @ChrisDodd: [Here](https://stackoverflow.com/q/70311736/12149471) is an example of a question in which violating the rules you mentioned did actually cause an error to occur. – Andreas Wenzel Jan 03 '22 at 21:26