0
#include <stdio.h>

int main() {
    FILE *fp;
    char ch = 'g';
    fp = fopen("temp.txt", "r+"); 
    while (feof(fp)) {
        fputc(ch,fp);
        fseek(fp, 1, SEEK_CUR);
   }
   fclose(fp);
}

As per this question, I want the code to update all the characters to 'g'. However I see that it does nothing.

chqrlie
  • 131,814
  • 10
  • 121
  • 189

2 Answers2

1

So you want to change all bytes in the file to the letter g. Doing this on a text stream portably and reliably is surprisingly difficult1. I suggest you open the stream in binary mode instead and do something like this:

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

int main(void) {
    char buf[4096];
    long len;
    FILE *fp;

    fp = fopen("temp.txt", "rb+"); 
    if (fp == NULL) {
        fprintf(stderr, "cannot open temp.txt: %s\n", strerror(errno));
        return 1;
    }
    while ((len = fread(buf, 1, sizeof buf, fp)) > 0) {
        fseek(fp, -len, SEEK_CUR);
        memset(buf, 'g', len);
        fwrite(buf, 1, len, fp);
        fseek(fp, 0, SEEK_CUR);
    }
    fclose(fp);
    return 0;
}

Your question is not completely clear: if by update all the characters to 'g' you mean to leave unchanged bytes that are not characters, such as newline markers, the code will be a bit more subtile. It is much simpler to write a filter that reads a stream and produces an output stream with the changes:

For example, here is a filter that changes all letters to g:

#include <ctype.h>
#include <stdio.h>

int main(void) {
    int c;

    while ((c = getchar()) != EOF) {
        if (isalpha(c))
            c = 'g';
        putchar(c);
    }
    return 0;
}

  1. One cannot use ftell() in text mode to compute the number of characters in the file. You must use binary mode:

C 7.21.9.4 the ftell function

Synopsis

#include <stdio.h>
  long int ftell(FILE *stream);

Description

The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read.

Returns

If successful, the ftell function returns the current value of the file position indicator for the stream. On failure, the ftell function returns −1L and stores an implementation-defined positive value in errno.

Even counting characters read with getc() and writing the same number of 'g's from the beginning of the file does not work: the newline sequences may use more than one byte and some of the file contents at the end may not be overwritten on some legacy systems.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • `size_t len; ... while ((len = fread(buf, 1, sizeof buf, fp)) > 0) { fseek(fp, -len, SEEK_CUR);` is problematic with `-unsigned_variable`. Recommend to use `long len`. – chux - Reinstate Monica Jul 14 '17 at 14:18
  • 1
    @chux: Excellent point! I originally had `int len;` and overlooked the unsigned arithmetic issue when I later changed it to `size_t`. This unsigned stuff is a mess. Had I compiled the program, my default warning levels would have caught this, but it is a real problem for beginners with default compiler settings. – chqrlie Jul 14 '17 at 14:36
1

A successful call to the fseek() function clears the end-of-file indicator for the stream

mentioned here. And also writing bytes to file extend its size. So first in some way we need to make sure that we are not going beyond the size of file. As answer by @chqril in which He is making sure of bounds by reading the file and then writing to same portion with altered data.

Here is my solution in which I first found the size of file and then writing it from start to end. It only works when each character in file take exactly one byte.

#include <stdio.h>

int main()
{
    unsigned char ch = 'g';
    FILE* fp = fopen("tmp.txt","rb+");

    if (fp == NULL)
    {
        printf("cannot open file\n");
        return -1;
    }

    //seek to last byte
    if (fseek(fp, 0, SEEK_END) != 0)
    {
        printf("error during seeking\n");
        fclose(fp);
        return -1;
    }

    /*get current offset relative to start.
     *this is file size in bytes
     */
    long size = ftell(fp);

    if (size < 0)
    {
        printf("error during ftell");
        fclose(fp);
        return -1;
    }

    //reset pointer to start of file
    rewind(fp);

    /*overwrite every byte
     *this only works if file
     *size is static
     */
    for (long i = 0; i < size; ++i)
    {
        fputc(ch, fp);
    }

    fclose(fp);

    return 0;
}
Kamil Mahmood
  • 527
  • 9
  • 22
  • The problem with this approach is `ftell()` does not return the number of characters in the file in text mode. You must use binary mode: **C 7.21.9.4 the `ftell` function** *... The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call;* – chqrlie Jul 14 '17 at 13:02