Sweeter Mann
Sweeter Mann

Reputation: 29

Why doesn't fputc update the chars in this snippet?

#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.

Upvotes: 0

Views: 216

Answers (2)

Kamil Mahmood
Kamil Mahmood

Reputation: 526

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;
}

Upvotes: 1

chqrlie
chqrlie

Reputation: 145277

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.

Upvotes: 1

Related Questions