How to change a line after read a line in file?

52 views Asked by At

This is my code. read a line and change it.

    char buf[SIZE];
    while (fgets(buf,SIZE,fp) != NULL)
    {
        to_upper(buf);   
        fseek(fp,-1L,SEEK_CUR);
        fputs(buf,fp);
        fseek(fp,1L,SEEK_CUR);
    }

I know that I can create another file to implement, I want to konw why the code doesn't work?

2

There are 2 answers

0
Jonathan Leffler On BEST ANSWER

As I noted in a comment, you need to capture the current read position before calling fgets(); then you reset that position and write the revised data, and you need a seek of some sort after the write so that you can read again.

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

static void to_upper(char *buffer)
{
    unsigned char u;

    while ((u = *buffer) != '\0')
        *buffer++ = toupper(u);
}

int main(int argc, char **argv)
{
    const char *fname = "input.txt";
    if (argc > 2)
    {
        fprintf(stderr, "Usage: %s [file]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if (argc == 2)
        fname = argv[1];
    FILE *fp = fopen(fname, "r+");

    if (fp == NULL)
    {
        fprintf(stderr, "%s: failed to open file '%s' for read/write: %d %s\n",
                argv[0], fname, errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    char buffer[1024];
    long pos = ftell(fp);

    while (fgets(buffer, sizeof(buffer), fp) != 0)
    {
        to_upper(buffer);
        fseek(fp, pos, SEEK_SET);
        fputs(buffer, fp);
        fseek(fp, 0, SEEK_CUR);
        pos = ftell(fp);
    }

    fclose(fp);
    return 0;
}

The error checking after the open succeeds is non-existent, but the checking before that is crucial to safe operation.

The C standard §7.21.5.3 The fopen function ¶7 says:

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.

The code shown is careful to use the 'no-op' fseek(fp, 0, SEEK_CUR) call after the write and before the next read.

0
Craig Estey On

A few issues ...

  1. We need to remember the before and after positions of each line. Doing an fseek by one character position doesn't work. That's because we're reading an entire line (that is of varying length).
  2. While we can do fputs in this particular instance, it's somewhat hazardous. Better to just do fputc.
  3. Note that the code works because doing read/seek/write needs an fflush but here we're doing read/seek/write/seek so no fflush is needed.

Here is the refactored code. It is annotated:

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

int
main(int argc,char **argv)
{
    char buf[10000];

    --argc;
    ++argv;

    if (argc != 1) {
        fprintf(stderr,"need filename\n");
        exit(1);
    }

    FILE *fp = fopen(*argv,"r+");
    if (fp == NULL) {
        perror(*argv);
        exit(1);
    }

    long posbef = 0;

    while (1) {
        // get a line
        if (fgets(buf,sizeof(buf),fp) == NULL)
            break;

        // get starting position of next line
        long posaft = ftell(fp);

        // get first char of line and convert to uppercase
        int chr = buf[0];
        chr = toupper((unsigned char) chr);

        // seek [back] to start of current line
        fseek(fp,posbef,SEEK_SET);

        // output updated character
        fputc(chr,fp);

        // ensure the char is output (?)
        // need fflush after an output and before an input
#if 0
        fflush(fp);
#endif

        // seek to the start of the next line
        fseek(fp,posaft,SEEK_SET);

        // this is now the start position of the current line for the next
        // loop iteration
        posbef = posaft;
    }

    fclose(fp);

    return 0;
}