Я пытаюсь понять getchar ()! = EOF

Я читаю язык программирования C и до сих пор все понял. Однако, когда я наткнулся на getchar() и putchar(), я не смог понять, как они используются, и, более конкретно, что делает следующий код.

main()
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);
}

Я понимаю функцию main(), объявление целого числа c и цикл while. Однако меня смущает условие внутри цикла while. Каков ввод в этом коде C и каков вывод.

Извините, если это простой и глупый вопрос, но я просто ищу простое объяснение, прежде чем я продолжу читать книгу и запутаюсь еще больше.


person Paolo Bernasconi    schedule 23.05.2012    source источник
comment
Он просто повторяет то, что вы вводите в терминал. Пока вы не дойдете до контрольного кода конца ввода. Ctrl + Z в Windows, Ctrl + D в * nix.   -  person Hans Passant    schedule 23.05.2012


Ответы (8)


Этот код можно более четко записать как:

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).]

person Oliver Charlesworth    schedule 23.05.2012
comment
@TheGameiswar, пожалуйста, прочтите и мое новое дополнение. - person Antti Haapala; 09.08.2017
comment
EOF не является персонажем. - person Ayxan Haqverdili; 21.07.2019

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 и выполнение завершится.

person Pablo Santa Cruz    schedule 23.05.2012
comment
Хороший пример полиморфизма с использованием getchar(). Читает из файла (ввод). Читает с клавиатуры (ввод) - person overexchange; 21.06.2015
comment
ты даже можешь ./your_program < input_file - person DevUt; 04.11.2016

Код, написанный в соответствии с текущими стандартами 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;
}

это читается как

  • repeat forever
    • get the next character ("byte") of input from standard input and store it into c
    • if no exceptional condition occurred while reading the said character
      • then output the character stored into c into standard output
    • else
      • break the loop

Многие языки программирования обрабатывают исключительные условия посредством создания исключений, которые нарушают нормальный поток программы. C не делает ничего подобного. Вместо этого функции, которые могут завершиться ошибкой, имеют возвращаемое значение, а любые исключительные условия сигнализируются специальным возвращаемым значением, которое вам необходимо проверить в документации данной функции. В случае getchar в документации стандарта C11 говорится (C11 7.21.7.6p3):

  1. Функция getchar возвращает следующий символ из входного потока, на который указывает stdin. Если поток находится в конце файла, для потока устанавливается индикатор конца файла, и getchar возвращает EOF. Если происходит ошибка чтения, устанавливается индикатор ошибки для потока, и getchar возвращает EOF.

В другом месте указано, что EOF - это целочисленная константа, равная ‹0, а любое обычное возвращаемое значение -> = 0 - unsigned char с расширением нуля до int.

Находящийся в конце файла поток означает, что весь ввод был использован. Для стандартного ввода это можно вызвать с клавиатуры, набрав Ctrl + D на терминалах Unix / Linux и Ctrl + Z < / kbd> в окнах консоли Windows. Другая возможность заключается в том, что программа будет получать ввод из файла или канала, а не с клавиатуры - тогда конец файла будет сигнализироваться всякий раз, когда этот ввод будет полностью использован, т.е.

cat file | ./myprogram

or

./myprogram < file

Как говорится в приведенном выше фрагменте, на самом деле есть два разных условия, которые могут привести к тому, что getchar вернет EOF: либо достигнут конец файла, или произошла фактическая ошибка. Это не может быть выведено только из возвращаемого значения. Вместо этого вы должны использовать функции feof и ferror. feof(stdin) вернет истинное значение, если на стандартном вводе будет достигнут конец файла. ferror(stdin) вернет истину, если произошла ошибка.

Если действительно произошла ошибка, переменная 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

Неверный файловый дескриптор или 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 - однако тут есть подводный камень - поскольку стандартный вывод буферизуется, нам нужно написать достаточно, чтобы буфер сбрасывался сразу, а не в конце программы. Получаем бесконечные нулевые байты от /dev/zero:

 % ./a.out < /dev/zero > /dev/full
 putchar failed: No space left on device

P.S. очень важно всегда использовать переменную типа int для хранения возвращаемого значения getchar(). Несмотря на то, что он читает символ, использование _45 _ / _ 46_ / plain char всегда неверно.

person Antti Haapala    schedule 08.08.2017
comment
@TonyTannous должен оседлать награду: D - person Antti Haapala; 09.08.2017
comment
Спасибо за ваши усилия и помощь - person TheGameiswar; 09.08.2017

Может быть, вас смутил тот факт, что ввод -1 в командной строке не завершает вашу программу? Поскольку getchar() читает это как два символа, - и 1. При присвоении c символ преобразуется в числовое значение ASCII. Это числовое значение хранится в некоторой области памяти, доступ к которой осуществляется c.

Затем putchar(c) извлекает это значение, просматривает таблицу ASCII и преобразует обратно в символ, который печатается.

Я полагаю, что найти десятичное значение -1 в таблице ASCII невозможно, потому что таблица начинается с 0. Таким образом, getchar() должен учитывать различные решения на разных платформах. может быть, есть getchar() версия для каждой платформы?

Мне просто странно, что этого EOF нет в обычном ascii. Это мог быть один из первых символов, которые нельзя распечатать. Например, конец строки находится в ASCII.

Что произойдет, если вы перенесете файл из Windows в Linux? Будет ли автоматически обновляться символ файла EOF?

person Pedro Norwego    schedule 21.10.2012
comment
Это больше похоже на новые вопросы, чем на настоящий ответ - person MestreLion; 25.02.2015

Функция getchar () считывает символ с клавиатуры (т. е. stdin )

В условии внутри заданного цикла while getchar() вызывается перед каждой итерацией, и полученное значение присваивается целому числу c.

Теперь необходимо понимать, что в C стандартный ввод (stdin) - это как файл. т.е. ввод буферизуется. Ввод будет оставаться в буфере до тех пор, пока он не будет фактически использован. stdin на самом деле является стандартным потоком ввода.

getchar() возвращает следующее доступное значение во входном буфере.

Программа по сути отображает все, что было прочитано с клавиатуры; включая пробелы, такие как \n (новая строка), пробел и т. д.

т.е. ввод - это ввод, который пользователь предоставляет с помощью клавиатуры (stdin обычно означает клавиатуру). А на выходе получается то, что мы предоставляем на входе.

Предоставляемый нами ввод читается посимвольно и обрабатывается как символы, даже если мы даем их как числа.

getchar() вернет EOF, только если будет достигнут конец файла. «Файл», который нас интересует, - это сам stdin (стандартный ввод).

Представьте, что существует файл, в котором сохраняется ввод, который мы вводим с клавиатуры. Это stdin. Этот «файл» похож на бесконечный файл. Так что нет EOF.

Если мы предоставим больше входных данных, чем то, что getchar() может обрабатывать за один раз (перед тем, как передать их в качестве входных данных нажатием клавиши ввода), дополнительные значения все равно будут сохраняться во входном буфере без использования. getchar() прочитает первый символ из ввода, сохранит его в c and printcwithputchar (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

eg:

while ((c = getchar()) != EOF)
{

   putchar(c);
   fflush(stdin);
}
printf("\nGot past!");

Если вы нажмете комбинацию клавиш, чтобы дать EOF, перед выходом из программы отобразится сообщение Got past!.

Если stdin не уже пуст, вам придется дважды нажать эту комбинацию клавиш. Один раз очистить этот буфер, а затем смоделировать EOF.

РЕДАКТИРОВАТЬ: дополнительная пара круглых скобок вокруг c = getchar() в while ((c = getchar()) != EOF) предназначена для того, чтобы значение, возвращаемое getchar(), было сначала присвоено c перед тем, как это значение сравнивается с EOF.

Если бы этой дополнительной круглой скобки не было, выражение фактически было бы while (c = (getchar() != EOF) ), что означало бы, что c могло иметь одно из двух значений: 1 (истина) или 0 (ложь), что явно не то, что предназначено.

person J...S    schedule 08.08.2017

 getchar()

получает символ из ввода.

 c = getchar()

Значение этого присвоения - это значение левой стороны после присвоения или значение прочитанного символа. Значение EOF по умолчанию -1.

 ((c = getchar()) != EOF)

Пока значение остается отличным от EOF или, другими словами, пока условие остается истинным, цикл будет продолжать повторяться. Как только значение станет EOF, значение всего условия будет 0, и цикл будет прерван.

Дополнительные круглые скобки вокруг c = getchar() предназначены для компилятора, чтобы подчеркнуть, что мы действительно хотели выполнить присвоение внутри условия, потому что обычно предполагается, что вы хотите ввести ==, и предупреждает вас.

 main() {
     int c;
     while ((c = getchar()) != EOF)
         putchar(c);
 }

Таким образом, весь код фактически повторяет то, что вы вводите. Он присваивает значение символов c внутри условия, а затем выводит его обратно в тело цикла, заканчиваясь только при обнаружении конца файла.

person Darinka Zobenica    schedule 29.12.2016
comment
Дополнительные скобки вокруг c = getchar () предназначены для компилятора, чтобы подчеркнуть, что мы не хотим назначать результат getchar ()! = EOF. - person Thomas G.; 08.08.2017

Аналогично | pipe, вы можете использовать перенаправление в своей системе, чтобы использовать приведенный выше код для отображения всего символьного содержимого файла, пока он не достигнет конца (EOF), представленного обычно CTRL-Z или CTRL-D.

В консоли: ProgramName < FileName1.txt

А чтобы создать копию того, что читается из FileName1, вы можете: ProgramName < FileName1.txt > CopyOfInput.txt

Это демонстрирует вашу программу разными способами, чтобы, надеюсь, помочь вам в понимании.

-Надеюсь, это поможет.

person Finding Nemo 2 is happening.    schedule 24.05.2012

main(){
int c;
while ((c = getchar()) != EOF)
   putchar(c);
}

Фактически c = getchar () предоставляет символ, который пользователь вводит в консоли, и это значение проверяется с помощью EOF, который представляет конец файла. EOF встречается в конце файла. (c = getchar ())! = EOF эквивалентно c! = EOF. Теперь я думаю, что это намного проще. Если у вас возникнут какие-либо вопросы, дайте мне знать.

person Shivam Goel    schedule 19.06.2016