читать () из стандартного ввода - PullRequest
16 голосов
/ 23 января 2012

Рассмотрим следующую строку кода:

while((n = read(STDIN_FILENO, buff, BUFSIZ)) > 0)

Насколько я понимаю, read/write функции являются частью небуферизованного ввода / вывода. Значит ли это, что read() функция будет читать только один символ за вызов из stdio? Или, другими словами, значение n будет

    -1  in case of error
n =  0  in case of EOF
     1  otherwise

Если это не тот случай, когда будет возвращена вышеуказанная функция read() и почему?

Примечание: я также думал, что read() будет ждать, пока он успешно прочитает BUFSIZ количество символов из стандартного ввода. Но что происходит в случае, если количество символов, доступных для чтения, меньше BUFSIZ? Будет ли чтение ждать вечно или пока не придет EOF (Ctrl + D в Unix или Ctrl + Z в Windows)?

Также, скажем, BUFSIZ = 100 и stdin = ACtrl+D (т. Е. EOF, следующий сразу за одним символом). Сколько раз while loop будет повторяться?

Ответы [ 5 ]

19 голосов
/ 23 января 2012

Способ чтения () зависит от того, что читается.Для обычных файлов, если вы запрашиваете N символов, вы получите N символов, если они доступны, меньше N, если конец файла вмешивается.

Если read () читает с терминала в каноническом / готовом режиме,Драйвер tty предоставляет данные по одной строке за раз.Поэтому, если вы скажете read (), чтобы получить 3 символа или 300, read будет зависать до тех пор, пока драйвер tty не увидит новую строку или определенный ключ EOF терминала, а затем read () вернется либо с количеством символов в строке, либо сколичество запрошенных вами символов, в зависимости от того, какое из значений меньше.

Если read () читает с терминала в неканоническом / необработанном режиме, чтение сразу же получит доступ к нажатиям клавиш.Если вы попросите read () получить 3 символа, он может вернуть от 0 до 3 символов в зависимости от времени ввода и конфигурации терминала.

read () будет вести себя по-разному в условиях сигналов, возвращаяс меньшим, чем запрошенное количество символов, или -1 с errno, равным EINTR, если сигнал прервал чтение до того, как поступили какие-либо символы.

read () будет вести себя по-другому, если дескриптор настроен для неблокированияI / O.read () вернет -1 с errno, установленным в EAGAIN или EWOULDBLOCK, если вход не был немедленно доступен.Это относится и к сокетам.

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

4 голосов
/ 23 января 2012

Ваш код гласит:

while((n = read(0, buff, BUFSIZ) != 0))

Это некорректно - скобки означают, что оно интерпретируется как:

while ((n = (read(0, buff, BUFSIZ) != 0)) != 0)

, где булево условие оценивается перед назначением, поэтому n получит только значения 0 (условие неверно) и 1 (условие истинно).

Вы должны написать:

while ((n = read(0, buff, BUFSIZ)) > 0)

Это останавливается на EOF или ошибке чтенияи n позволяет узнать, с каким условием вы столкнулись.


По-видимому, приведенный выше код был опечаткой в ​​вопросе.

Небуферизованный ввод / вывод будет считывать до числасимволов, которые вы читаете (но не более).Это может читать меньше из-за EOF или ошибки.Это может также читать меньше, потому что меньше доступно во время звонка.Рассмотрим терминал;как правило, это будет читать только до конца строки, потому что нет больше доступных, чем это.Рассмотрим трубу;если в процессе подачи было сгенерировано 128 непрочитанных байтов, то если BUFSIZ равен 4096, вы получите только 128 байтов от чтения.Неблокирующий дескриптор файла может вернуться, потому что ничего не доступно;сокет может вернуть меньше байтов, потому что еще нет доступной информации;чтение с диска может вернуть меньше байтов, поскольку при выполнении чтения в файле остается меньше запрошенного количества байтов.

В общем, однако, read() не вернет только один байт, если вызапросить много байтов.

2 голосов
/ 23 января 2012

Как указано на странице read():

Возвращаемое значение

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

Таким образом, каждый read() будет читать до числа указанных байтов; но это может читать меньше. «Небуферизованный» означает, что если вы укажете read(fd, bar, 1), чтение будет читать только один байт. Буферизованный ввод-вывод пытается прочитать в BUFSIZ квантах, даже если вам нужен только один символ. Это может показаться расточительным, но позволяет избежать дополнительных затрат на выполнение системных вызовов, что делает его быстрым.

0 голосов
/ 23 января 2012

Когда мы говорим, что read небуферизовано, это означает, что буферизация на уровне вашего процесса не происходит после того, как данные извлечены из описания открытого открытого файла, которое является потенциально общим ресурсом. Если stdin является терминалом, вероятно, в игре есть по крайней мере 2 дополнительных буфера:

  1. Терминальный буфер, который, вероятно, может хранить 1-4k данных вне линии до.
  2. Буфер обработанного / канонического режима ядра для ввода / редактирования строки на терминале, который позволяет пользователю выполнять примитивное редактирование (backspace, backword, erase line и т. Д.) В строке до ее отправки (в буфер, описанный выше) нажав Enter.

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

0 голосов
/ 23 января 2012
  1. чтение пытается получить все запрошенные символы.
  2. если EOF происходит до того, как все запрошенные символы могут быть возвращены, он возвращает то, что получил после этого следующее чтение возвращает -1, чтобы вы знали, что конец файла.

То, что происходит, когда он пытается читать, и там ничего нет, включает в себя нечто, называемое блокировкой. Вы можете позвонить, чтобы открыть блокировку файла или неблокирование. «блокирование» означает ожидание, пока есть что вернуть.

Это то, что вы видите в оболочке, ожидающей ввода. Он сидит там. Пока вы не нажмете возврат.

Неблокирующая означает, что чтение не будет возвращать байты данных, если их нет. В зависимости от множества других факторов, которые сделали бы совершенно правильный ответ непригодным для вас, read установит в errno что-то вроде EWOULDBLOCK, что позволит вам узнать, почему ваше чтение вернуло ноль байтов. Это не обязательно фатальная ошибка.

Ваш код может проверяться на минус, чтобы найти EOF или ошибки

...