Использование fseek с указателем файла, указывающим на стандартный ввод

В зависимости от аргументов командной строки я устанавливаю указатель файла так, чтобы он указывал либо на указанный файл, либо на стандартный ввод (для передачи по конвейеру). Затем я передаю этот указатель ряду различных функций для чтения из файла. Вот функция для получения указателя файла:

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

Когда он указывает на стандартный ввод, fseek не работает. Под этим я подразумеваю, что использую его, а затем использую fgetc и получаю неожиданные результаты. Это ожидаемое поведение, и если да, то как мне перемещаться в разные места в потоке?

Например:

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

person Tyler Treat    schedule 07.02.2011    source источник
comment
ваш пример кода выглядит хорошо для меня. (кроме случаев, когда файл не существует, но это не имеет отношения к вашей проблеме)   -  person J-16 SDiZ    schedule 07.02.2011
comment
мне кажется правильным. что за компилятор? вы можете попытаться напечатать внутри функции bar() оба указателя (stdin и myFile), чтобы убедиться, что они одинаковы.   -  person leonbloy    schedule 07.02.2011
comment
@leonbloy: я обнаружил, что проблема на самом деле с fseek(). Очевидно, это не работает, когда указатель указывает на стандартный ввод? Есть мысли по этому поводу? (обновил вопрос)   -  person Tyler Treat    schedule 07.02.2011
comment
связанные: stackoverflow.com/questions/2502489/ | stackoverflow.com/questions/40063488/how- делать-и-искать-в-стандартном вводе   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 17.11.2019


Ответы (3)


Да, это совершенно нормально, что fseek не работает с stdin — обычно он работает только с файлом на диске или чем-то подобным.

Хотя на самом деле это POSIX, вы обычно можете использовать if (isatty(fileno(myFile))), чтобы получить хотя бы довольно хорошее представление о том, будет ли работать поиск в конкретном файле. В некоторых случаях isatty и/или fileno будут иметь начальное подчеркивание (например, IIRC версии, поставляемые с компиляторами Microsoft).

person Jerry Coffin    schedule 07.02.2011
comment
Тогда я думаю, что мой вопрос - и это может быть глупо - как мне перейти к другому индексу в потоке? Я думаю, что было бы более элегантное решение, чем вызов fgetc(myFile) в цикле n раз? - person Tyler Treat; 07.02.2011
comment
@Tyler Treat: нет - это фундаментальное различие между потоками и файлами. Концептуально ввод из потока нигде не сохраняется — поток эфемерен, генерируется по мере того, как вы его потребляете. Если вам нужно просмотреть данные из потока, вы должны прочитать их в память и искать там (или, если они особенно велики, прочитать их во временный файл). - person caf; 07.02.2011
comment
Я не нахожу поддержки в спецификации C11 относительно того, что fseek не работает на стандартном вводе. У вас есть спецификация C для поддержки этого, когда stdin является текстовым потоком? - person chux - Reinstate Monica; 10.09.2015

Fseek() основан на lseek(), и на справочной странице lseek обсуждаются возможные ошибки, в том числе:

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

Если stdin подключен к псевдотерминалу, я полагаю, что он будет иметь поведение сокета.

person DigitalRoss    schedule 07.02.2011
comment
Говорит ли ANSI C что-нибудь об этом? - person Ciro Santilli 新疆再教育营六四事件ۍ 22.05.2015

Вот соответствующая запись в стандарте ANSI, касающаяся функции fseek:

Для текстового потока либо смещение должно быть равно нулю, либо смещение должно быть значением, возвращаемым более ранним успешным вызовом функции ftell в потоке, связанном с тем же файлом, и откуда должно быть SEEK_SET.

Так что возможно, но с некоторыми ограничениями

person Jorge    schedule 31.03.2019