Каково обоснование того, что fread / fwrite принимает размер и считается аргументом? - PullRequest
86 голосов
/ 17 ноября 2008

У нас здесь на работе обсуждалось, почему fread и fwrite принимают размер на элемент и считают и возвращают количество прочитанных / записанных элементов, а не просто принимают буфер и размер. Единственное использование, которое мы могли бы придумать, - это если вы хотите прочитать / записать массив структур, которые не делятся равномерно выравниванием платформы и, следовательно, дополняются, но это не может быть настолько распространенным, чтобы оправдать этот выбор в дизайне.

С FREAD (3) :

Функция fread () читает nmemb элементов данных, каждый размер в байтах, из потока, указанного потоком, сохраняя их в указанном месте по ptr.

Функция fwrite () записывает nmemb элементов данных, каждый размер в байтах долго, к потоку, указанному потоком, получая их из локации данный ptr.

fread () и fwrite () возвращают количество успешно прочитанных или записанных элементов (т. е. не количество символов). Если происходит ошибка, или достигнут конец файла, возвращаемое значение - короткий счетчик элементов (или ноль).

Ответы [ 7 ]

64 голосов
/ 17 ноября 2008

Разница между fread (buf, 1000, 1, stream) и fread (buf, 1, 1000, stream) заключается в том, что в первом случае вы получите только один кусок из 1000 байт или nuthin, если файл меньше а во втором случае вы получите все в файле меньше и до 1000 байт.

19 голосов
/ 17 ноября 2008

Он основан на том, как реализовано fread .

В спецификации UNIX указано:

Для каждого объекта размер вызовов должен быть сделано для функции fgetc () и результаты сохраняются в порядке чтения в массив неподписанных символов наложение объекта.

fgetc также имеет это примечание:

Поскольку fgetc () работает с байтами, чтение символа, состоящего из несколько байтов (или "многобайтовый символ ") может потребовать нескольких вызовов в fgetc ().

Конечно, это предшествует причудливым кодировкам переменных байтов, таким как UTF-8.

SUS отмечает, что это на самом деле взято из документов ISO C.

12 голосов
/ 19 сентября 2010

Это чистые предположения, однако в те времена (некоторые все еще существуют) многие файловые системы не были простыми потоками байтов на жестком диске.

Многие файловые системы основаны на записях, поэтому для эффективного удовлетворения таких файловых систем вам нужно будет указать количество элементов («записей»), позволяя fwrite / fread работать с хранилищем как с записями, а не просто байтовые потоки.

9 голосов
/ 17 ноября 2008

Здесь, позвольте мне исправить эти функции:

size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
    return fread( ptr, 1, size, stream);
}


size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
    return fwrite( ptr, 1, size, stream);
}

Что касается обоснования параметров для fread() / fwrite(), я давно потерял свою копию K & R, так что я могу только догадываться. Я думаю, что вероятный ответ заключается в том, что Керниган и Ричи, возможно, просто подумали, что выполнение бинарного ввода-вывода будет наиболее естественным образом выполнено для массивов объектов. Кроме того, они, возможно, думали, что блок ввода-вывода будет быстрее / проще реализовать или что-то еще на некоторых архитектурах.

Несмотря на то, что стандарт C указывает, что fread() и fwrite() должны быть реализованы в терминах fgetc() и fputc(), помните, что стандарт появился на свет намного позже того, как C был определен K & R, и что вещи, указанные в Стандартов не могло быть в оригинальных дизайнерских идеях. Возможно даже, что сказанное в «Языке программирования C» от ​​K & R может не совпадать с тем, что было при разработке языка.

Наконец, вот что П. Дж. Плаугер говорит о fread() в «Стандартной библиотеке C»:

Если size (второй) аргумент больше единицы, вы не можете определить может ли функция считывать до size - 1 дополнительных символов сверх того, что она сообщает. Как правило, вам лучше вызывать функцию как fread(buf, 1, size * n, stream); вместо fread(buf, size, n, stream);

По сути, он говорит, что интерфейс fread() не работает. В отношении fwrite() он отмечает, что «ошибки записи, как правило, встречаются редко, поэтому это не является существенным недостатком» - с этим утверждением я бы не согласился.

3 голосов
/ 17 ноября 2008

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

1 голос
/ 11 августа 2018

Наличие отдельных аргументов для размера и количества может быть полезным для реализации, которая может избежать чтения любых частичных записей. Если бы нужно было использовать однобайтовые чтения из чего-то вроде конвейера, даже если бы они использовали данные фиксированного формата, нужно было бы предусмотреть возможность разделения записи на два чтения. Если бы мог вместо запросов, например. неблокирующее чтение до 40 записей по 10 байтов каждая, когда доступно 293 байта, и система возвращает 290 байтов (29 целых записей), оставляя 3 байта готовыми для следующего чтения, что было бы гораздо удобнее.

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

1 голос
/ 18 апреля 2012

Я думаю, это потому, что в C отсутствует перегрузка функций. Если бы они были, размер был бы излишним. Но в C вы не можете определить размер элемента массива, вы должны указать его.

Учтите это:

int intArray[10];
fwrite(intArray, sizeof(int), 10, fd);

Если fwrite принимает число байтов, вы можете написать следующее:

int intArray[10];
fwrite(intArray, sizeof(int)*10, fd);

Но это просто неэффективно. Вы будете иметь размер в (int) раз больше системных вызовов.

Еще один момент, который следует учитывать, это то, что вы обычно не хотите, чтобы часть элемента массива записывалась в файл. Вы хотите целое число или ничего. fwrite возвращает количество успешно написанных элементов. Итак, если вы обнаружите, что написано только 2 младших байта элемента, что бы вы сделали?

В некоторых системах (из-за выравнивания) вы не можете получить доступ к одному байту целого числа без создания копии и сдвига.

...