Очень большой эквивалент fseek для стандартного ввода?

У меня есть очень большое известное количество байтов на стандартном вводе, и я хочу отбросить большое (также известное) их количество, прежде чем читать интересующую часть (другими словами, я хочу выполнить fseek вперед на большое целое число, но fseek не работает). не определено для труб). Самый простой способ добиться этого — большое количество вызовов fgetc, а первая альтернатива — использовать один вызов fread с большим временным указателем, выделенным для хранения результата. Первый очень медленный, а второй использует потенциально неограниченный объем памяти без уважительной причины. Выполнение нескольких операций чтения меньшего размера решает проблему неограниченного использования памяти, но вводит свободный параметр (размер фрагмента), который, вероятно, имеет разное самое быстрое значение для каждой комбинации машины и ОС.

Существуют ли какие-либо альтернативы, которые достигают этой цели аккуратным и эффективным способом? Предполагается POSIX.


person campbell    schedule 21.09.2013    source источник


Ответы (1)


Нет возможности «пропустить» данные на канале — их нужно прочитать.

Если это очень большой блок, вы захотите использовать буфер среднего размера (как компромисс между накладными расходами и использованием памяти), что-то вроде этого:

 size_t dataToRead = some_large_number;

 while(dataToRead)
 {
    char buffer[4096];
    size_t toread = min(sizeof(buffer), dataToRead);
    size_t nread = fread(buffer, 1, toread, stdin);
    dataToRead -= nread;
 }

Размер, 4096, является довольно произвольным выбором, но он достаточно велик, чтобы не вызывать ОГРОМНОЕ количество операций чтения на входе, и достаточно мал, чтобы не использовать сумасшедшие объемы пространства стека. Маловероятно, что вы сильно выиграете/потеряете от изменения этого размера.

person Mats Petersson    schedule 21.09.2013
comment
@CharlieBurns: Да, я думал об этом, а затем отвлекся, чтобы убедиться, что я правильно получил аргументы fread (поскольку я всегда хочу поместить файл в качестве первого аргумента и никогда не могу вспомнить, что такое количество и размер в середина...) - person Mats Petersson; 22.09.2013
comment
Я исправил свой комментарий, как только вы его исправили. Без проблем. Это было очевидное упущение. - person Charlie Burns; 22.09.2013
comment
Ваш 4096 - это бесплатный параметр из моего первоначального вопроса. Предположительно, есть преимущество в скорости из-за размера страницы или рационального отношения к размеру страницы на большинстве интересующих архитектур. Однако не будет ли быстрее выделить буфер, а не объявлять для него место в стеке? Гарантирует ли последнее какое-либо выравнивание? - person campbell; 22.09.2013
comment
У него может быть некоторое преимущество в размере страницы, но он не выравнивается автоматически. Скорее всего, не стоит вызывать malloc, так как это НАМНОГО длиннее, чем одна инструкция, необходимая для освобождения места в стеке, и чтобы получить это обратно, вам нужно получить довольно много - я сомневаюсь, что fread не будет так лучше. - person Mats Petersson; 22.09.2013
comment
Это имеет смысл, хотя, если вы собираетесь повторно использовать один и тот же буфер для многих таких операций поиска того же или меньшего размера, по-видимому, есть момент, когда единственный вызов malloc() того стоит. Есть ли разница в скорости между fread(buffer, 1, toread, stdin) и fread(buffer, toread, 1, stdin)? Еще одно небольшое исправление к вашему ответу: min() не определен для целых чисел в C. - person campbell; 22.09.2013
comment
Я почти уверен, что где-то size и count все равно перемножаются для чтения, поэтому не должно быть разницы между 1, toread и toread, 1. Я не вижу абсолютно никаких причин, почему вы должны malloc буфер - это вообще не имеет значения - read который является базовым системным вызовом, будет считываться в буфер ядра, который затем копируется. Пока он выровнен примерно до 4 или 16 байтов [что будет автоматически], нет причин для дальнейшего выравнивания. - person Mats Petersson; 23.09.2013