Я пытаюсь понять getchar()!= EOF
Я читаю язык программирования C и все понял до сих пор.
Однако, когда я наткнулся на getchar()
и putchar()
, я не понял, что их использует, и, более конкретно, что делает следующий код.
main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
Я понимаю функцию main()
, объявление целых чисел c
и цикла while
. Тем не менее я смущен состоянием внутри цикла while
. Каков вход в этот код C и какой результат.
Извините, если это основной и глупый вопрос, но я просто ищу простое объяснение, прежде чем двигаться дальше в книге и запутаться.
Ответы
Ответ 1
Этот код можно записать более четко:
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)
.]
Ответ 2
getchar()
- это функция, которая считывает символ из стандартного ввода. EOF
- это специальный символ, используемый в C, чтобы указать, что достигнут КОНЕЦ ФАЙЛА.
Обычно вы получите символ EOF
, возвращающийся из getchar()
, когда ваш стандартный ввод отличается от консоли (т.е. файла).
Если вы запустите свою программу в unix следующим образом:
$ cat somefile | ./your_program
Затем ваш getchar()
вернет каждый символ в somefile
и EOF
, как только закончится somefile
.
Если вы запустите свою программу следующим образом:
$ ./your_program
И отправьте EOF
через консоль (нажав CTRL+D
в Unix или CTRL + Z в Windows), тогда getchar()
также вернет EOF
, и выполнение закончится.
Ответ 3
Может быть, вы запутались в том, что ввод -1 в командной строке не заканчивается вашей программой? Поскольку getchar()
читает это как два символа, - и 1. В присваивании c символ преобразуется в числовое значение ASCII. Это числовое значение сохраняется в некоторой ячейке памяти, доступ к которой осуществляется с помощью c.
Затем putchar(c)
извлекает это значение, просматривает таблицу ASCII и преобразует обратно в символ, который печатается.
Я думаю, что найти значение -1 десятичного числа в таблице ASCII невозможно, потому что таблица начинается с 0. Таким образом, getchar()
должен учитывать различные решения на разных платформах. может быть, есть версия getchar()
для каждой платформы?
Мне просто кажется странным, что этот EOF не находится в обычном ascii. Это мог быть один из первых символов, которые нельзя распечатать. Например, End-of-line находится в ASCII.
Что произойдет, если вы перенесите свой файл из окон в linux? Будет ли автоматически обновляться символ файла EOF?
Ответ 4
Код, написанный с текущими стандартами C, должен быть
#include <stdio.h>
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
, в стандартный вывод
- еще
Многие языки программирования обрабатывают исключительные условия путем создания исключений, которые нарушают нормальный поток программы. C не делает этого. Вместо этого функции, которые могут выйти из строя, имеют возвращаемое значение, и любые исключительные условия сигнализируются специальным возвращаемым значением, которое необходимо проверить из документации данной функции. В случае getchar
в документации по стандарту C11 говорится (C11 7.21.7.6p3):
- Функция
getchar
возвращает следующий символ из входного потока, на который указывает stdin
. Если поток находится в конце файла, индикатор конца файла для потока установлен и getchar
возвращает EOF
. Если возникает ошибка чтения, отображается индикатор ошибки для потока, а getchar
возвращает EOF
.
В другом месте указано, что EOF
является целочисленной константой, 0, и любое обычное возвращаемое значение >= 0 - unsigned char
с нулевым продолжением до int
.
Поток, находящийся в конце файла, означает, что весь вход был потреблен. Для стандартного ввода это можно вызвать с клавиатуры, набрав Ctrl + D на терминалах Unix/Linux и Ctrl + Z в окнах консоли Windows. Другая возможность заключалась бы в том, чтобы программа получала входные данные из файла или канала, а не из клавиатуры, - тогда конец файла будет сигнализироваться всякий раз, когда этот вход полностью потребляется, т.е.
cat file | ./myprogram
или
./myprogram < file
Как сказано выше, существуют фактически два разных условия, которые могут привести к возврату getchar
EOF
: либо был достигнут конец файла, либо произошла фактическая ошибка. Это не может быть выведено только из возвращаемого значения. Вместо этого вы должны использовать функции feof
и ferror
. feof(stdin)
вернет истинное значение, если на стандартном входе достигнут конец файла. ferror(stdin)
вернет true, если произошла ошибка.
Если произошла фактическая ошибка, переменная errno
, определенная <errno.h>
, будет содержать код ошибки; функция perror
может использоваться для автоматического отображения сообщения с возможностью чтения человеком с префиксом. Таким образом, мы могли бы расширить пример до
#include <stdio.h>
#include <errno.h> // for the definition of errno
#include <stdlib.h> // 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
Чтобы вызвать ошибку, это немного сложнее. В оболочках bash
и zsh
стандартный ввод может быть закрыт, чтобы он не происходил нигде, добавив <&-
в командную строку:
% ./a.out <&-
An error occurred. errno set to 9
Human readable explanation: Bad file descriptor
Дескриптор Bad файла или 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
P.S. очень важно всегда использовать переменную типа int
для хранения возвращаемого значения getchar()
. Несмотря на то, что он читает символ, с использованием signed
/unsigned
/plain char
всегда ошибочно.
Ответ 5
Функция getchar() считывает символ с клавиатуры (т.е. stdin
)
В условии внутри заданного цикла while
перед каждой итерацией вызывается getchar()
, а принятое значение присваивается целому числу c
.
Теперь нужно понимать, что в C стандартный ввод (stdin
) является как файл. т.е. вход буферизуется. Вход останется в буфере, пока он фактически не будет потреблен.
stdin
- фактически стандартный поток ввода.
getchar()
возвращает следующее доступное значение во входном буфере.
Программа по существу отображает все, что было прочитано с клавиатуры; включая пробел, например \n
(новая строка), пробел и т.д.
т.е. вход - это вход, который пользователь предоставляет с помощью клавиатуры (stdin
обычно означает клавиатуру).
И вывод - это то, что мы предоставляем в качестве входных данных.
Ввод, который мы предоставляем, считывается символом по символу и обрабатывается как символы, даже если мы даем их как числа.
getchar()
вернет EOF
только в том случае, если достигнут конец файла. "Файл, который нам касается здесь, это сам stdin
(стандартный ввод).
Представьте себе файл, в котором хранится ввод, который мы предоставляем с помощью клавиатуры. Thats stdin
.
Этот файл похож на бесконечный файл. Поэтому no 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()
. Он записывает вывод в стандартный выходной поток (stdout
, обычно монитор).
EOF
не является символом, присутствующим в файле. Его что-то возвращает функция как код ошибки.
Вероятно, вы, вероятно, не сможете выйти из цикла give 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
уже not уже пуст, вам придется дважды нажать эту комбинацию клавиш. Как только очистите этот буфер, а затем, чтобы имитировать EOF
.
EDIT: дополнительная пара скобок вокруг c = getchar()
в while ((c = getchar()) != EOF)
заключается в том, чтобы удостовериться, что значение, возвращаемое getchar()
, сначала присваивается c
до , что значение сравнивается с EOF
.
Если этой дополнительной круглой скобки не было, выражение могло бы быть while (c = (getchar() != EOF) )
, что означало бы, что c
может иметь одно из двух значений: 1
(для true) или 0
(для false), который, очевидно, не является тем, что предполагается.
Ответ 6
Аналогично | pipe выше вы можете использовать перенаправление в своей системе, чтобы использовать вышеприведенный код, чтобы отображать все содержимое символа файла, пока он не достигнет конца (EOF), представленного CTRL-Z или CTRL-D обычно.
В консоли:
ProgramName < FileName1.txt
И чтобы создать копию того, что читается из FileName1, вы можете:
ProgramName < FileName1.txt > CopyOfInput.txt
Это демонстрирует вашу программу несколькими способами, чтобы, надеюсь, помочь вам понять.
-Удача, которая помогает.
Ответ 7
main(){
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
На самом деле с = GetChar() предоставляет символ, который пользователь вводит на консоли, и это значение проверяется с EOF, которая представляет конец файла. EOF встречается в конце файла. (С = GetChar())!= EOF эквивалентно с!= EOF. Теперь я думаю, что это намного проще. Если вам нужен дальнейший запрос, дайте мне знать.
Ответ 8
getchar()
получает символ от ввода.
c = getchar()
Значение этого присваивания - это значение левой стороны после присвоения или значение прочитанного символа. Значение EOF
по умолчанию -1
.
((c = getchar()) != EOF)
Пока значение остается чем-то отличным от EOF
или, другими словами, до тех пор, пока условие остается истинным, цикл будет продолжать итерацию. Как только значение станет EOF
, значение всего условия будет 0
, и оно сломает цикл.
Дополнительные скобки вокруг c = getchar()
предназначены для компилятора, чтобы подчеркнуть, что мы действительно хотели выполнить назначение внутри условия, потому что обычно предполагается, что вы хотите набрать ==
и предупредить вас.
main() {
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
Таким образом, весь код на самом деле перекликается с тем, что вы вводите. Он присваивает значение символов c
внутри условия, а затем выводит его обратно в тело цикла, заканчивая только тогда, когда обнаружен конец файла.