Я пытаюсь понять 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 ):

  1. Функция 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 внутри условия, а затем выводит его обратно в тело цикла, заканчивая только тогда, когда обнаружен конец файла.

Давайте будем гением компьютера.