Я пытаюсь понять getchar ()! = EOF
Я читаю язык программирования C и все понял до сих пор. Однако, когда я столкнулся с getchar()
и putchar()
, я не понял, что их использует, и, более конкретно, что делает следующий код.
main() { int c; while ((c = getchar()) != EOF) putchar(c); }
Я понимаю функцию main()
, объявление целого c
и цикла while. Тем не менее, я смущен состоянием внутри цикла while. Каков вход в этот код C, и каков результат.
Извините, если это основной и глупый вопрос, но я просто ищу простое объяснение, прежде чем двигаться дальше в книге и запутаться.
Этот код можно написать более четко:
main() { int c; while (1) { c = getchar(); // Get one character from the input if (c == EOF) { break; } // Exit the loop if we receive EOF ("end of file") putchar(c); // Put the character to the output } }
Символ EOF
принимается, когда ввода больше нет. Имя имеет смысл в том случае, когда ввод считывается из реального файла, а не для ввода пользователем (что является частным случаем файла).
[В стороне, как правило, main
функция должна быть записана как int main(void)
.]
Функция getchar()
– это функция, которая считывает символ из стандартного ввода . EOF
– специальный символ, используемый в C, чтобы указать, что достигнут КОНЕЦ ФАЙЛА .
Обычно вы получите символ EOF
возвращающийся из getchar()
когда ваш стандартный ввод отличается от консоли (т. getchar()
).
Если вы запустите свою программу в unix следующим образом:
$ cat somefile | ./your_program
Тогда ваш getchar()
будет возвращать каждый отдельный символ в somefile
и EOF
как только заканчивается somefile
.
Если вы запустите свою программу следующим образом:
$ ./your_program
И отправьте EOF
через консоль (нажав CTRL+D
в Unix или CTRL + Z в Windows), то getchar()
также вернет EOF
и выполнение закончится.
Может быть, вы запутались в том, что ввод -1 в командной строке не заканчивается вашей программой? Поскольку getchar()
читает это как два символа, – и 1. В присваивании c символ преобразуется в числовое значение ASCII. Это числовое значение сохраняется в некоторой ячейке памяти, доступ к которой осуществляется с помощью c.
Затем putchar(c)
извлекает это значение, просматривает таблицу ASCII и преобразует обратно в символ, который печатается.
Я думаю, что найти значение -1 десятичного числа в таблице ASCII невозможно, потому что таблица начинается с 0. Поэтому getchar()
должен учитывать различные решения на разных платформах. может быть, есть версия getchar()
для каждой платформы?
Мне просто кажется странным, что этот EOF не находится в обычном ascii. Это мог быть один из первых символов, которые нельзя распечатать. Например, End-of-line находится в ASCII.
Что произойдет, если вы перенесите файл из окон в Linux? Будет ли автоматически изменен символ файла EOF?
Код, написанный с использованием текущих стандартов С, должен быть
#include int main(void) { int c; while ((c = getchar()) != EOF) putchar(c); }
Цикл можно переписать как
int c; while (1) { c = getchar(); if (c != EOF) putchar(c); else break; }
это читается как
- повторять навсегда
- получить следующий символ («байт») ввода со стандартного ввода и сохранить его в
c
- если исключительное условие не произошло при чтении указанного символа
- затем выводят символ, сохраненный в
c
в стандартный вывод
- затем выводят символ, сохраненный в
- еще
- разбить петлю
- получить следующий символ («байт») ввода со стандартного ввода и сохранить его в
Многие языки программирования обрабатывают исключительные условия за счет выделения исключений, которые нарушают нормальный stream программы. C не делает этого. Вместо этого функции, которые могут выйти из строя, имеют возвращаемое значение, и любые исключительные условия сигнализируются специальным возвращаемым значением, которое необходимо проверить из документации данной функции. В случае getchar
в документации по стандарту C11 говорится ( C11 7.21.7.6p3 ):
- Функция
getchar
возвращает следующий символ из входного streamа, на который указываетstdin
. Если stream находится в конце файла, устанавливается индикатор конца файла для streamа, иgetchar
возвращаетEOF
. Если возникает ошибка чтения, отображается индикатор ошибки для streamа, иgetchar
возвращаетEOF
.
В другом месте указано, что EOF
представляет собой целочисленную константу, которая равна <0, а любое обычное возвращаемое значение равно> = 0 – unsigned char
нуль, расширенный до int
.
Поток, находящийся в конце файла, означает, что весь вход был потреблен. Для стандартного ввода это можно вызвать с клавиатуры, набрав Ctrl + D на терминалах Unix / Linux и Ctrl + Z в Windows-консолях Windows. Другая возможность заключалась бы в том, чтобы программа получала входные данные из файла или канала, а не из клавиатуры, – тогда конец файла будет сигнализироваться всякий раз, когда этот вход полностью потребляется, т. Е.
cat file | ./myprogram
или
./myprogram < file
Как сказано выше, существует фактически два разных условия, которые могут заставить getchar
возвращать EOF
: либо достигнут конец файла , либо произошла фактическая ошибка. Это не может быть выведено только из возвращаемого значения. Вместо этого вы должны использовать функции feof
и ferror
. feof(stdin)
вернет истинное значение, если на стандартном входе достигнут конец файла. ferror(stdin)
вернет true, если произошла ошибка.
Если произошла фактическая ошибка, переменная errno
определенная
будет содержать код ошибки; функция perror
может использоваться для автоматического отображения сообщения с возможностью чтения человеком с префиксом. Таким образом, мы могли бы расширить этот пример до
#include #include // for the definition of errno #include // for exit() int main(void) { int c; while ((c = getchar()) != EOF) putchar(c); if (feof(stdin)) { printf("end-of-file reached\n"); exit(0); } else if (ferror(stdin)) { printf("An error occurred. errno set to %d\n", errno); perror("Human readable explanation"); exit(1); } else { printf("This should never happen...\n"); exit('?'); } }
Чтобы запустить конец файла, можно использовать Ctrl + D (здесь отображается как ^D
) в новой строке в Linux:
% ./a.out Hello world Hello world ^D end-of-file reached
(обратите внимание, как вход здесь буферизируется по строке, поэтому вход не чередуется в строке с выходом).
Аналогичным образом, мы можем получить тот же эффект, используя конвейер.
% echo Hello world | ./a.out Hello world end-of-file reached
Вызов ошибки несколько сложнее. В shellх bash
и zsh
стандартный ввод может быть закрыт, чтобы он не происходил нигде, добавив <&-
в командную строку:
% ./a.out <&- An error occurred. errno set to 9 Human readable explanation: Bad file descriptor
Плохой файловый дескриптор или EBADF
означает, что стандартный дескриптор ввода- файла 0 был недопустимым, так как он вообще не был открыт.
Еще один интересный способ генерации ошибки - прочитать стандартный ввод из каталога - это приводит к тому, что errno устанавливается в EISDIR
в Linux:
% ./a.out < / An error occurred. errno set to 21 Human readable explanation: Is a directory
На самом деле нужно также проверить возвращаемое значение putchar
- оно также возвращает EOF
при ошибке или символ, написанный:
while ((c = getchar()) != EOF) { if (putchar(c) == EOF) { perror("putchar failed"); exit(1); } }
И теперь мы можем протестировать это, перенаправив стандартный вывод в /dev/full
- однако есть gotcha - поскольку стандартный вывод буферизирован, нам нужно написать достаточно, чтобы буфер сразу же скрылся, а не в конце программы. Мы получаем бесконечные нулевые байты от /dev/zero
:
% ./a.out < /dev/zero > /dev/full putchar failed: No space left on device
PS очень важно всегда использовать переменную типа int
для хранения возвращаемого значения getchar()
. Несмотря на то, что он читает символ , использование символа signed
/ unsigned
/ plain всегда ошибочно .
Функция getchar () считывает символ с клавиатуры (т. е. stdin
)
В условии внутри заданного цикла while getchar()
вызывается перед каждой итерацией, а принятое значение присваивается целому c
.
Теперь нужно понимать, что в C стандартный ввод ( stdin
) подобен файлу. т.е. вход буферизуется. Вход останется в буфере, пока он фактически не будет потреблен. stdin
– фактически стандартный stream ввода .
getchar()
возвращает следующее доступное значение во входном буфере.
Программа по существу отображает все, что было прочитано с клавиатуры; включая пробел типа \n
(новая строка), пробел и т. д.
т. е. вход – это вход, который пользователь предоставляет с помощью клавиатуры (стандартная клавиша означает клавиатуру). И выход – это то, что мы предоставляем в качестве входных данных.
Ввод, который мы предоставляем, считывается символом по символу и обрабатывается как символы, даже если мы даем их как числа.
getchar()
возвращает EOF
только в том случае, если достигнут конец файла. «Файл», который нам касается здесь, – это сам stdin
(стандартный ввод).
Представьте себе файл, в котором хранится ввод, который мы предоставляем с помощью клавиатуры. Это stdin
. Этот «файл» похож на бесконечный файл . Так что нет EOF
.
Если мы предоставляем больше ввода, чем getchar()
может обрабатывать одновременно (прежде чем давать его как ввод, нажав клавишу ввода), дополнительные значения будут по-прежнему сохранены в исходном буфере без потерь. getchar()
будет читать первый символ из ввода, хранить его в c and print
c with
putchar (c) `.
Во время следующей итерации цикла while дополнительные символы, заданные во время предыдущей итерации, которые все еще находятся в stdin
, принимаются во while ((c = getchar()) != EOF)
с частью c=getchar()
. Теперь тот же процесс повторяется до тех пор, пока в входном буфере ничего не останется.
Это заставляет его выглядеть так, как если putchar()
возвращает строку вместо одного символа за раз, если в качестве итерации вводится более одного символа.
Например: если вход был
abcdefghijkl
выход был бы таким же
abcdefghijkl
Если вы не хотите этого поведения, вы можете добавить fflush (stdin); сразу после putchar(c);
, Это заставит цикл печатать только первый символ на входе, предоставляемом во время каждой итерации.
Например: если вход был
adgbad
будет напечатан только a
.
Вход отправляется на stdin
только после нажатия клавиши ввода.
putchar () – это противоположность getchar()
. Он записывает вывод в стандартный выходной stream (стандартный вывод, обычно монитор).
EOF
не является символом, присутствующим в файле. Это что-то, возвращаемое функцией как код ошибки.
Однако вы, вероятно, не сможете выйти из цикла while. Входной буфер будет опустошен (для отображения на выходе), как только что-то попадет в него с клавиатуры, а stdin
не даст EOF
.
Для ручного выхода из цикла EOF
можно отправить с помощью клавиатуры, нажав ctrl + D в Linux и
ctrl + Z в Windows
например:
while ((c = getchar()) != EOF) { putchar(c); fflush(stdin); } printf("\nGot past!");
Если вы нажмете комбинацию клавиш, чтобы дать EOF
, сообщение Got past!
будет отображаться перед выходом из программы.
Если stdin
еще не пуст, вам придется дважды нажать эту комбинацию клавиш. Как только очистите этот буфер, а затем, чтобы имитировать EOF
.
EDIT: дополнительная пара скобок вокруг c = getchar()
в while ((c = getchar()) != EOF)
должна убедиться, что значение, возвращаемое getchar()
, сначала назначается c
до того, как это значение сравнивается с EOF
.
Если этой дополнительной круглой скобки не было, выражение было бы эффективно while (c = (getchar() != EOF) )
что означало бы, что c
может иметь одно из двух значений: 1
(для true) или 0
(для false), что явно не то, что предназначено.
Подобным образом | pipe выше вы можете использовать redirect в своей системе, чтобы использовать вышеприведенный код, чтобы отображать все содержимое символа файла, пока он не достигнет конца (EOF), представленного CTRL-Z или CTRL-D обычно.
В консоли: ProgramName < FileName1.txt
А для создания копии того, что читается из FileName1, вы можете: ProgramName < FileName1.txt > CopyOfInput.txt
Это демонстрирует вашу программу несколькими способами, чтобы, надеюсь, помочь вашему пониманию.
-Надеюсь, это поможет.
main(){ int c; while ((c = getchar()) != EOF) putchar(c); }
На самом деле c = getchar () предоставляет символ, который пользователь вводит на консоль, и это значение проверяется с помощью EOF, который представляет End Of File. EOF встречается в конце файла. (c = getchar ())! = EOF эквивалентно c! = EOF. Теперь я думаю, что это намного проще. Если вам нужен дальнейший запрос, дайте мне знать.
getchar()
получает символ от ввода.
c = getchar()
Значение этого присваивания – это значение левой стороны после присвоения или значение символа, который был прочитан. Значение EOF
по умолчанию -1
.
((c = getchar()) != EOF)
Пока значение остается чем-то иным, чем EOF
или, другими словами, пока условие остается истинным, цикл будет продолжать итерацию. Как только значение станет EOF
значение всего условия будет равно 0
и он сломает цикл.
Дополнительные скобки вокруг c = getchar()
предназначены для компилятора, чтобы подчеркнуть, что мы действительно хотели выполнить присвоение внутри условия, потому что обычно предполагается, что вы хотите ввести ==
и предупреждает вас.
main() { int c; while ((c = getchar()) != EOF) putchar(c); }
Таким образом, весь код на самом деле перекликается с тем, что вы вводите. Он присваивает значение символам c
внутри условия, а затем выводит его обратно в тело цикла, заканчивая только тогда, когда обнаружен конец файла.