Очистить или очистить файловый дескриптор без чтения ()?

(Примечание. Это не вопрос о том, как очистить write(). Это, так сказать, другой конец.)

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

Если это невозможно в POSIX, есть ли в каких-либо операционных системах какие-либо непереносимые способы сделать это?

ОБНОВЛЕНИЕ: обратите внимание, что я говорю о файловых дескрипторах, не потоках.


person Teddy    schedule 17.12.2009    source источник


Ответы (8)


Для потоков доступна функция fclean, которая очищает буфер записи и возвращает буфер чтения обратно в систему ввода-вывода.

http://www.gnu.org/software/hello/manual/libc/Cleaning-Streams.html

Если вы действительно хотите пропустить байты, правильным действием будет изменение положения указателя файла. Просто пропустите столько байтов, сколько не хотите читать.

http://www.gnu.org/software/hello/manual/libc/File-Position-Primitive.html#File-Position-Primitive

person jdmichal    schedule 17.12.2009
comment
fclean определенно нестандартен с языковой точки зрения - person ; 17.12.2009
comment
Правильный. Это часть GNU libc. Я почти уверен, что это доступно практически во всех ОС. Я обновил ссылки на руководство GNU, а не на зеркало. - person jdmichal; 17.12.2009
comment
Преобладающий ответ, кажется, - ищу. Я приму твой ответ, так как ты был первым, кто его предложил. Я надеялся на что-то, что будет работать с каналами, FIFO и т.д., поэтому я ждал, но, думаю, такого не существует. - person Teddy; 28.12.2009
comment
lseek - лучшее решение для реальных файлов, но определенные файловые дескрипторы недоступны для поиска (например, каналы и FIFO), поэтому выполнение lseek не удастся. Если вам нужно наиболее общее решение, которое работает со всеми типами файловых дескрипторов, вам действительно нужно прочитать его во временный буфер и отбросить. Для оптимальной производительности используйте достаточно большой буфер, но не настолько большой, чтобы вы взорвали кэш L1 или L2. - person Adam Rosenfield; 29.12.2009
comment
Ах я вижу. Я согласен с комментарием Адама, что если lseek не работает, то на самом деле единственный выход - это прочитать и сбросить данные. - person jdmichal; 05.01.2010
comment
Этот ответ можно улучшить: первая ссылка, которая, как я предполагал, будет иметь дело с вышеупомянутым fclean, вместо этого говорит о fflush(). Я не мог найти ничего на fclean в другом месте. Во втором абзаце даже не упоминается соответствующая функция (которая оказывается lseek()). Следовательно, весь ответ несколько сбивает с толку и полностью зависит от связанного содержимого. - person domsson; 16.05.2020

Если вы имеете дело с tty, обратите внимание на tcflush():

#include <termios.h>
int tcflush(int fildes, int queue_selector);

После успешного завершения tcflush () отбрасывает данные, записанные в объект, на который указывает fildes (дескриптор открытого файла, связанный с терминалом), но не переданные, или данные, полученные, но не прочитанные, в зависимости от значения queue_selector [...]

http://opengroup.org/onlinepubs/007908775/xsh/tcflush.html

person squelart    schedule 02.06.2010
comment
Спасибо, именно то, что я искал! - person Etheryte; 09.06.2015

Для POSIX используйте lseek(2) или _ 2_ для поиска вперед. Для Windows используйте SetFilePointer() или _ 4_.

person Adam Rosenfield    schedule 27.12.2009

Если вы знаете, сколько байтов нужно пропустить, вы можете сделать lseek(fd, n, SEEK_CUR); для систем POSIX. Также есть fseek() для FILE * объектов. В POSIX, я думаю, вы можете безопасно искать за пределами конца файла, идея заключается в том, что если позже будет записано больше данных, чтобы данные прошли мимо позиции, установленной с помощью lseek(), вы сможете прочитать больше данных сейчас.

person Alok Singhal    schedule 27.12.2009

Linux 2.6.17 или более поздняя версия с библиотекой GNU C версии 2.5 или более поздней содержит системный вызов splice(), который можно использовать для отправки данных из одного файлового дескриптора в другой без копирования их в пространство пользователя. Это можно использовать для отбрасывания данных, просто открыв /dev/null и spliceing данные из дескриптора исходного файла в /dev/null файловый дескриптор.

person Teddy    schedule 30.09.2015

Ни read (), ни flush () не являются частью Standard C или C ++, но, разумеется, ни одна из стандартных функций не поддерживает очистку входных потоков. Я предполагаю, что это отражает то, чего нет в основных операционных системах. Обычный способ полностью избежать чтения - это пропустить это с помощью какой-либо функции seek ().

person Community    schedule 17.12.2009

Согласно this, система POSIX сделает это на fflush(stream);.

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

person Chinmay Kanchi    schedule 17.12.2009
comment
Это управляет (а) синхронизацией позиций чтения и записи при чередовании буферизованного ввода-вывода в одном файле и (б) отменой эффектов буфера чтения, который выполнялся раньше запрошенного. Не то, что просит OP. - person ephemient; 18.12.2009
comment
Это для потоков, а не для дескрипторов файлов. - person Teddy; 18.12.2009

BSD представила fpurge(), Solaris и glibc представила __fpurge().
Из справочной страницы:

#include <stdio.h>
#include <stdio_ext.h>
void  __fpurge(FILE *stream);

Функция __fpurge () очищает буферы данного потока. Для потоков вывода отбрасывается любой незаписанный вывод. Для входных потоков это отбрасывает любой ввод, прочитанный из базового объекта, но еще не полученный через getc (3); это включает любой текст, возвращаемый через ungetc (3).

Функция __fpurge () делает то же самое, но без возврата значения.

Однако обратите внимание:

Эти функции нестандартны и непереносимы. Функция fpurge () была введена в 4.4BSD и недоступна в Linux. Функция __fpurge () была введена в Solaris и присутствует в glibc 2.1.95 и новее.

Если вы работаете с файловым дескриптором, вы можете сначала получить FILE* из дескриптора.

int fd;
/* ... */
FILE* fp = fdopen(fd, "r");
/* ... */
__fpurge(fp);
person domsson    schedule 16.05.2020
comment
Не думаю, что это поможет. Он удалит все, что находится в собственных буферах stdio программы (которые, если вы только что запустили fdopen'ed, будут ничем), но не повлияют на данные, доступные для чтения из ОС. - person Nate Eldredge; 17.05.2020