Using fseek with a file pointer that points to stdin
Asked Answered
E

3

14

Depending on command-line arguments, I'm setting a file pointer to point either towards a specified file or stdin (for the purpose of piping). I then pass this pointer around to a number of different functions to read from the file. Here is the function for getting the file pointer:

FILE *getFile(int argc, char *argv[]) {
    FILE *myFile = NULL;
    if (argc == 2) {
        myFile = fopen(argv[1], "r");
        if (myFile == NULL)
           fprintf(stderr, "File \"%s\" not found\n", argv[1]);
    }
    else
        myFile = stdin;
    return myFile;
}

When it's pointing to stdin, fseek does not seem to work. By that, I mean I use it and then use fgetc and I get unexpected results. Is this expected behavior, and if so, how do I move to different locations in the stream?

For example:

int main(int argc, char *argv[]) {
    FILE *myFile = getFile(argc, argv); // assume pointer is set to stdin
    int x = fgetc(myFile); // expected result
    int y = fgetc(myFile); // expected result
    int z = fgetc(myFile); // expected result

    int foo = bar(myFile); // unexpected result

    return 0;
}

int bar(FILE *myFile) {
    fseek(myFile, 4, 0);
    return fgetc(myFile);
}
Erzurum answered 7/2, 2011 at 3:11 Comment(4)
your example code looks fine for me. (except when the file not exist, but this is unrelated to your problem)Evy
seems right to me. what compiler is it? you might try to print, inside the bar() function both pointers (stdin and myFile) to check they are the same.Fosterfosterage
@leonbloy: I have discovered that the problem is actually with fseek(). Apparently it does not work when the pointer is pointing to stdin? Any thoughts on this? (Updated the question)Erzurum
related: #2502989 | https://mcmap.net/q/829741/-how-do-i-seek-in-stdinSoares
G
18

Yes, it's perfectly normal that fseek won't work on stdin -- it'll normally only work on a disk file, or something reasonably similar.

Though it's really a POSIX thing, you can typically use if (isatty(fileno(myFile))) to get at least a pretty good idea of whether seeking will work in a particular file. In some cases, isatty and/or fileno will have a leading underscore (e.g., IIRC the versions provided with Microsoft's compilers do).

Geier answered 7/2, 2011 at 3:27 Comment(6)
Then I guess my question is -- and this might be stupid -- how would I go about moving to a different index in the stream? I would think there would be a more elegant solution than calling fgetc(myFile) in a loop n times?Erzurum
@Tyler Treat: There isn't - this the fundamental difference between streams and files. Conceptually, the input from a stream isn't stored anywhere - a stream is ephemeral, generated as you consume it. If you need to seek around in the data from a stream, you should read it into memory and seek around there (or, if it's particularly large, read it into a temporary file).Thinking
I do not find support in the C11 spec concerning "it's perfectly normal that fseek won't work on stdin". Do you have C specification to support that when stdin is a text stream?Mapp
Change the incorrect "fseek won't work on stdin" to "fseek won't work on pipes, sockets, or FIFOs". Here is an example where fseek works perfectly well on stdin because stdin is coming from a (seekable) file: ./a.out <a.outMacrophysics
Minor nit w.r.t. the comment from @IanD.Allen In this case, stdin is not "coming from" a seekable file; it is more correct to say that stdin is a seekable file.Benzofuran
To clarify my previous comment. The issue with the phrase "coming from" is that it evokes an image of a pipe and fosters the misconception that the data is somehow in motion. People will often think of cmd < file and having the same behavior as ... | cmd, but it is very different. It is probably more correct to say that "stdin is associate with a regular file" than to say that "stdin is a regular file", but that same language would apply to f = fopen(path, mode); and you would say that "f is associated with path".Benzofuran
E
3

Fseek() is based on lseek(), and the lseek man page discusses possible errors, including:

 [ESPIPE]           Fildes is associated with a pipe, socket, or FIFO.

If stdin is connected to a pseudo tty, I believe it will have socket behavior.

Enneahedron answered 7/2, 2011 at 3:28 Comment(1)
Does ANSI C say anything about it?Soares
T
0

Here is the relevant entry in the ANSI standard concerning the fseek function:

For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET

So, possible but with some limitations

Thereupon answered 31/3, 2019 at 22:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.