Существует ли функция getline
, которая использует fread
(блок ввода / вывода) вместо fgetc
(символьный ввод / вывод)?
Производительность снижается при чтении файла символ за символом через fgetc
. Мы думаем, что для повышения производительности мы можем использовать чтение блоков через fread
во внутреннем цикле getline
. Тем не менее, это приводит к потенциально нежелательному эффекту чтения после конца строки. По крайней мере, для этого потребуется реализация getline
для отслеживания «непрочитанной» части файла, что требует абстракции, выходящей за пределы семантики ANSI C FILE. Это не то, что мы хотим реализовать сами!
Мы профилировали наше приложение, и низкая производительность изолирована от того факта, что мы потребляем большие файлы символ за символом через fgetc
. Для сравнения, остальная часть накладных расходов имеет тривиальную стоимость. Мы всегда последовательно читаем каждую строку файла, от начала до конца, и мы можем заблокировать весь файл на время чтения. Это, вероятно, облегчает реализацию fread
на основе *1013*.
Итак, существует ли функция getline
, которая использует fread
(блочный ввод / вывод) вместо fgetc
(символьный ввод / вывод)? Мы уверены, что это так, но если нет, то как мы должны это реализовать?
Обновление Найдена полезная статья, Обработка пользовательского ввода в C , автор Paul Hsieh. Это подход, основанный на fgetc
, но в нем есть интересное обсуждение альтернатив (начиная с того, насколько плох gets
, затем обсуждая fgets
):
С другой стороны, обычная реплика программистов на С (даже тех, кого считают опытными) состоит в том, что 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
(и не усекающая ввод)?