Home > Enterprise >  opening and closing a text file many times and read from the continuation
opening and closing a text file many times and read from the continuation

Time:06-01

I have a text file, and I open it and read one line of it, and close the text file. I'm calling my function under a for loop, but each time this function reads the first line of a text file, how can I fix it to read from the continuation

CodePudding user response:

You can use fseek to reposition yourself in the file after closing and reopening, but it is very unusual to do so. So unusual, in fact, that I would suggest it is completely wrong. Here's some sample code that demonstrates how to do that, as well as a more typical loop. Each loop here reads the first 2 lines of the file, assuming each line is sufficiently small; handling long lines is beyond the scope of this question.

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

FILE * xfopen(const char *path, const char *mode);
void xfseek(FILE *stream, long offset, int whence, const char *);
long xftell(FILE *stream, const char *);
void xfclose(FILE *stream, const char *);


int
main(int argc, char *argv[])
{
    const char *path = argc > 1 ? argv[1] : "input";

    /* Read the first two lines of the file, closing the file on each
     * iteration. This is ** not ** the usual way to do this, and is
     * included here for demonstration
     * purposes only.  DO NOT DO THIS.
     * It is very unusual to close and re-open the file on each iteration.
     */
    long position = 0;
    for( int line = 1; line < 3; line   ){
        FILE *ifp = xfopen(path, "r");
        char buf[1024];
        xfseek(ifp, position, SEEK_SET, path);
        fgets(buf, sizeof buf, ifp);  /* (1) */
        printf("line %d: %s", line, buf);
        position = xftell(ifp, path);
        xfclose(ifp, path);  /* !! */
    }

    /* The more usual way to read each line of a file is to simply
     * read it with repeated calls to the appropriate read method
     * (fgets, fread, fgetc, etc.)  Each subsequent read starts
     * where the previous read finished.
     */
    FILE *ifp = xfopen(path, "r");
    for( int line = 1; line < 3; line   ){
        char buf[1024];
        fgets(buf, sizeof buf, ifp);  /* (1) */
        printf("line %d: %s", line, buf);
    }
    xfclose(ifp, path);
    return 0;
}



FILE *
xfopen(const char *path, const char *mode)
{
    FILE *fp = path[0] != '-' || path[1] != '\0' ? fopen(path, mode) :
        *mode == 'r' ? stdin : stdout;
    if( fp == NULL ){
        perror(path);
        exit(EXIT_FAILURE);
    }
    return fp;
}


void
xfseek(FILE *stream, long offset, int whence, const char *name)
{
    if( fseek(stream, offset, whence) == -1){
        perror(name);
        exit(EXIT_FAILURE);
    }
}

long
xftell(FILE *stream, const char *name)
{
    long ret = ftell(stream);
    if( ret == -1 ){
        perror(name);
        exit(EXIT_FAILURE);
    }
    return ret;
}

void
xfclose(FILE *stream, const char *name)
{
    if( fclose(stream) ){
        perror(name);
        exit(EXIT_FAILURE);
    }
}

Notes: (1) It is left as an exercise for the reader how best to handle short reads (eg, when fgets returns NULL) or long lines (eg, when fgets completely fills the buffer but fails to read an entire line). Perhaps it is a bit of a cop-out to leave that as an exercise, but the annoyance of dealing with those issues points strongly towards reasons for using the standard idiom. If you want to print the first two lines of the file, use some variation of for( int count = 0; (c = fgetc(fp)) != NULL && count < 2; ) { if( c == '\n' ) count = 1; putchar(c); }. Putting the read function as a condition of the loop is (almost) always the best choice.

CodePudding user response:

Regarding all the dialog in comments regarding whether it is the right approach or not to do what you are asking, it seems pretty clear that your stated ask is adamant about using fseek() et. all to view new lines when opening and closing a file, so I am offering the following as one way it can be done...

In a nutshell, to open and close a file, and each time access and display a different line, you must first know where each of the locations to be viewed are located within that file. Indeed, as you have tagged, fseek(), (as well as ftell()) is a feasible way to do this. A simple and conceptual set of pseudo code steps to do this might look like this:

//store locations of each line:
FILE *fp = fopen(fn, "r");
if(fp)
{
    for(int i = 0; i < l_cnt; i  )
    {
        pos[i] = ftell(fp);
        fgets(line, sizeof line, fp);
    }
}
fclose(fp);

Then...

//alternately open and close file to view successive lines at stored positions
for(int i = 0; i < line_cnt; i  )
{
    FILE *fp = fopen(fn, "r");
    if(fp)
    {
        fseek(fp, pos[i], 0);
        fgets(line, sizeof line, fp);
        printf("line %d: %s\n", i, line);
        fclose(fp);
    }       
} 

There is a more complete source and run-time example here

  • Related