Есть ли функция getline
, которая использует fread
(блочный ввод-вывод) вместо fgetc
(символ I / O)?
Чтение файла посимвольно через fgetc
снижает производительность. Мы думаем, что для повышения производительности мы можем использовать чтение блоков через fread
во внутреннем цикле getline
. Однако это приводит к потенциально нежелательному эффекту чтения после конца строки.По крайней мере, для этого потребуется реализация getline
для отслеживания «непрочитанной» части файла, что требует абстракции за пределами семантики ANSI C FILE. Это не то, что мы хотим реализовать сами!
Мы профилировали наше приложение, и низкая производительность связана с тем, что мы посимвольно потребляем большие файлы через fgetc
. По сравнению с остальными накладными расходами затраты на них весьма незначительны. Мы всегда последовательно читаем каждую строку файла, от начала до конца, и можем заблокировать весь файл на время чтения. Это, вероятно, упрощает реализацию getline
на основе fread
.
Итак, существует ли функция getline
, которая использует fread
(блочный ввод-вывод) вместо fgetc
(символьный ввод-вывод)? Мы почти уверены, что это так, но если нет, то как нам это реализовать?
Обновление Нашел полезную статью Обработка пользовательского ввода в C Пола Хси. Это подход, основанный на fgetc
, но в нем есть интересное обсуждение альтернатив (начиная с того, насколько плох становится
, затем обсуждают fgets
):
С другой стороны, обычное возражение программистов C (даже тех, кто считается опытным) состоит в том, что в качестве альтернативы следует использовать fgets () . Конечно, сама по себе fgets () не обрабатывает ввод пользователя как таковой.Помимо странного условия завершения строки (при обнаружении \ n или EOF, но не \ 0), механизм, выбранный для завершения, когда буфер достиг своей емкости, состоит в том, чтобы просто резко остановить операцию fgets () и \ 0 прекратить это. Поэтому, если пользовательский ввод превышает длину предварительно выделенного буфера, fgets () возвращает частичный результат. Чтобы справиться с этим, у программистов есть несколько вариантов; 1) просто иметь дело с усеченным пользовательским вводом (нет способа сообщить пользователю, что ввод был усечен, пока они предоставляют ввод) 2) Имитируйте растущий массив символов и заполняйте его последовательными вызовами fgets () . Первое решение почти всегда является очень плохим решением для пользовательского ввода переменной длины, потому что большую часть времени буфер неизбежно будет слишком большим из-за того, что он пытается захватить слишком много обычных случаев и слишком мал для необычных случаев. Второе решение подойдет, за исключением того, что его сложно реализовать правильно. Ни один из них не рассматривает странное поведение fgets ' по отношению к' \ 0 '.
Упражнение, оставленное читателю: чтобы определить, сколько байтов было действительно прочитано при вызове fgets () , можно попытаться сканировать, как это происходит, на предмет '\ n' и пропускать любой '\ 0', не превышая при этом размер, переданный в fgets () . Объясните, почему этого недостаточно для самой последней строки потока.Какая слабость ftell () мешает ему полностью решить эту проблему?
Упражнение, оставленное читателю: Решите проблему определения длины данных, потребляемых fgets () с помощью перезапись всего буфера ненулевым значением между каждым вызовом fgets () .
Таким образом, с fgets () у нас остается выбор: написать много кода и жить с условием завершения строки, которое несовместимо с остальной частью библиотеки C, или иметь произвольный вырез. выключенный. Если этого недостаточно, то с чем мы останемся? scanf () смешивает синтаксический анализ с чтением таким образом, чтобы его нельзя было разделить, а fread () будет читать после конца строки. Короче говоря, библиотека C не оставляет нам ничего. Мы вынуждены делать собственные проверки на основе fgetc () напрямую. Итак, давайте попробуем.
Итак, существует ли функция getline
, основанная на fgets
(и не усекающая ввод)?