Что такое EOF и как его вызвать? [закрыто]

12

Это мой C исходный код.

Когда я собираю его в Ubuntu, он начинает получать символы, но я не знаю, как завершить программу, поскольку она не заканчивается вводом ENTERили возвратом каретки.

Что означает EOF? Как я могу вызвать это?

Этот источник также есть в книге Денниса Ритчи:

#include <stdio.h>
    /* count digits, white space, others */
main ()
{
  int c, i, nwhite, nother;
  int ndigit[10];
  nwhite = nother = 0;
  for (i = 0; i < 10; ++i)
    ndigit[i] = 0;
  while ((c = getchar ()) != EOF)
    if (c >= '0' && c <= '9')
      ++ndigit[c - '0'];
    else if (c == ' ' || c == '\n' || c == '\t')
      ++nwhite;
    else
      ++nother;
  printf ("digits =");
  for (i = 0; i < 10; ++i)
    printf (" %d", ndigit[i]);
  printf (", white space = %d, other = %d\n", nwhite, nother);
}
stackprogramer
источник
4
на языке C -1является эквивалентом EOF. Он определен /usr/include/stdio.hкак макроконстанция
Эдвард Торвальдс
1
Соответствующее чтение: stackoverflow.com/q/12389518/3701431
Сергей Колодяжный
@edwardtorvalds ввод в -1качестве ввода не работает :)
Сергей Колодяжный
Я думаю, что та же самая книга Денниса Ричи объясняет это.
andy256
Также актуально: unix.stackexchange.com/questions/110240/… (Ни один из ответов на этот вопрос не является полностью правильным.)
fkraiem

Ответы:

22

Tl; др

Как правило, вы можете «запустить EOF» в программе, запущенной на терминале нажатием клавиши CTRL+ Dсразу после последней очистки ввода.


Что означает EOF? Как я могу вызвать это?

EOF означает конец файла.

«Запуск EOF» в данном случае примерно означает «информирование программы о том, что ввод больше не будет отправлен».

В этом случае, так getchar()как вернет отрицательное число, если ни один символ не прочитан, выполнение прекращается.

Но это относится не только к вашей конкретной программе, это относится ко многим различным инструментам.

В общем, «запуск EOF» может быть выполнен нажатием клавиши CTRL+ Dсразу после последнего сброса ввода (т. Е. Путем отправки пустого ввода).

Например с cat:

% cat >file # Hit ENTER
foo # Hit ENTER and CTRL+D
% 

При нажатии CTRL+ Dпроисходит то, что ввод, введенный с момента последнего сброса ввода, сбрасывается; когда это пустой вход, read()системный вызов, вызванный из STDIN программы 0, getchar()возвращает отрицательное число ( -1в библиотеке GNU C), и это, в свою очередь, интерпретируется как EOF 1 .


1 - /programming//a/1516177/4316166

кос
источник
2
Компиляция работает, потому что разделение запятыми не связано с нахождением в одной строке. Кроме этого, отличное объяснение EOF :)
Paulius Šukys
@ PauliusŠukys Да, ты прав. Мой C немного ржавый. :)
Кос
1
iirc EOF не определено как -1 для стандарта. Это как раз то, что происходит в glibc, например.
щенок
1
EOF не «состоит в отправке« пустого ввода »», и ответ SO, который вы цитируете, не говорит об обратном. Это внеполосный сигнал. В случае терминала его отправляют, набрав Ctrl / d.
user207421
4

TL; DR : EOF - это не символ, это макрос, используемый для оценки отрицательного возврата функции чтения ввода. Можно использовать Ctrl+, Dчтобы отправить EOTсимвол, который заставит функцию вернуть-1

Каждый программист должен RTFM

Давайте обратимся к «Справочному руководству CA» Harbison and Steele, 4-е изд. с 1995 г., стр. 317:

Отрицательное целое число EOF - это значение, которое не является кодировкой «реального символа». , , Например, fget (раздел 15.6) возвращает EOF в конце файла, потому что нет «реального символа» для чтения.

По сути EOFэто не символ, а скорее целое значение реализуется в stdio.hпредставлять -1. Таким образом, ответ Коса является правильным, но речь идет не о получении «пустого» ввода. Важно отметить, что здесь EOF служит для сравнения возвращаемого значения (of getchar()), а не для обозначения фактического символа. Поддержки, man getcharкоторые:

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

fgetc (), getc () и getchar () возвращают символ, прочитанный как символ без знака, приведенный к int или EOF в конце файла или ошибки.

get () и fgets () возвращают s в случае успеха и NULL в случае ошибки или когда происходит конец файла, когда символы не были прочитаны.

ungetc () возвращает c в случае успеха или EOF в случае ошибки.

Рассмотрим whileцикл - его основная цель - повторить действие, если условие в скобках выполнено . Посмотри снова:

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

По сути, он говорит, что продолжайте делать что-то, если c = getchar()возвращает успешный код ( 0или выше; кстати, это обычное дело, попробуйте echo $?выполнить успешную команду, затем не удалось echo $?и посмотрите числа, которые они возвращают). Поэтому, если мы успешно получим символ и добавим C, возвращенный код состояния будет равен 0, а ошибка - -1. EOFопределяется как -1. Поэтому, когда -1 == -1возникает условие , цикл останавливается. И когда это произойдет? Когда нет больше персонажа, чтобы получить, когда c = getchar()не удается. Вы могли бы написать, while ((c = getchar ()) != -1)и это все еще будет работать

Кроме того, давайте вернемся к фактическому коду, вот выдержка из stdio.h

/* End of file character.
   Some things throughout the library rely on this being -1.  */
#ifndef EOF
# define EOF (-1)
#endif

Коды ASCII и EOT

Хотя символ EOF не является действительным символом, тем не менее, существует символ EOT(Конец передачи), который имеет десятичное значение ASCII 04; он связан с ярлыком Ctrl+ D(представлен также как метасимвол ^D). Характер окончания передачи использовался для обозначения закрытия потока данных в обратном направлении, когда компьютеры использовались для контроля телефонных соединений, отсюда и название «конец передачи».

Таким образом, можно отправить это значение ascii в программу следующим образом, обратите внимание на то, $'\04'что является EOT:

skolodya@ubuntu:$ ./a.out  <<< "a,b,c $'\04'"                                  
digits = 1 0 0 0 1 0 0 0 0 0, white space = 2, other = 9

Таким образом, мы можем сказать, что он существует, но это не для печати

Примечание

Мы часто забываем, что в прошлом компьютеры не были такими универсальными - дизайнеры должны использовать каждую доступную клавиатуру. Таким образом, отправка EOTсимвола с помощью CtrlD по-прежнему является «отправкой символа», в отличие от ввода прописной буквы A, ShiftA, вы все равно заставляете компьютер вводить данные с помощью доступных клавиш. Таким образом, EOT - это реальный символ в том смысле, что он исходит от пользователя, он читается компьютером (хотя не для печати, не виден людьми), он существует в компьютерной памяти

Комментарий Byte Commander

Если вы попытаетесь прочитать из / dev / null, это также должно вернуть EOF, верно? Или что я получу там?

Да, совершенно верно, потому /dev/nullчто нет никакого действительного символа, который нужно прочитать, следовательно, он c = getchar()вернет -1код, и программа сразу же закроется. Снова команда не возвращает EOF. EOF - это просто постоянная переменная, равная -1, которую мы используем для сравнения кода возврата функции getchar . EOFне существует как символ, это просто статическое значение внутри stdio.h.

Демо-версия:

# cat /dev/null shows there's no readable chars
DIR:/xieerqi
skolodya@ubuntu:$ cat /dev/null | cat -A        

# Bellow is simple program that will open /dev/null for reading. Note the use of literal -1                                   
   DIR:/xieerqi
skolodya@ubuntu:$ cat readNull.c                                               
#include<stdio.h>

void main()
{
   char c;
    FILE *file;
    file = fopen("/dev/null", "r");

    if (file) 
    {
    printf ("Before while loop\n");
        while ((c = getc(file)) != -1)
            putchar(c);
    printf("After while loop\n"); 
    fclose(file);
    }
}

DIR:/xieerqi
skolodya@ubuntu:$ gcc readNull.c -o readNull                                   

DIR:/xieerqi
skolodya@ubuntu:$ ./readNull
Before while loop
After while loop

Еще один гвоздь в гробу

Иногда пытаются доказать, что EOF - это символ с кодом, подобным этому:

#include <stdio.h>
int main(void)
{
    printf("%c", EOF);
    return 0;
}

Проблема в том, что тип данных char может иметь значение со знаком или без знака. Кроме того, они являются наименьшим адресуемым типом данных, что делает их очень полезными в микроконтроллерах, где память ограничена. Таким образом, вместо декларации int foo = 25;обычно можно увидеть в микроконтроллерах с небольшим объемом памяти char foo = 25;или что-то подобное. Кроме того, символы могут быть подписаны или не подписаны .

Можно проверить, что размер в байтах с программой, как это:

#include <stdio.h>
int main(void)
{
    printf("Size of int: %lu\n",sizeof(int));
    printf("Sieze of char: %lu\n",sizeof(char));
    //printf("%s", EOF);
    return 0;
}

skolodya@ubuntu:$ ./EOF                                                        
Size of int: 4
Sieze of char: 1

Какой именно смысл? Дело в том, что EOF определяется как -1, но тип данных char может печатать целочисленные значения .

OK . , .то, что если мы попытаемся напечатать char как строку?

#include <stdio.h>
int main(void)
{
    printf("%s", EOF);
    return 0;
}

Очевидно, это ошибка, но, тем не менее, ошибка скажет нам кое-что интересное:

skolodya @ ubuntu: $ gcc EOF.c -o EOF
EOF.c: в функции 'main': EOF.c: 4: 5: предупреждение: формат '% s' ожидает аргумент типа 'char *', но аргумент 2 имеет тип 'int' [-Wformat =] printf ("% s", EOF);

Шестнадцатеричные значения

Печать EOF в виде шестнадцатеричного значения дает FFFFFFFF16-битное (8-байтовое) значение, два комплимента a -1.

#include <stdio.h>
int main(void)
{
    printf("This is EOF: %X\n", EOF);
    printf("This is Z: %X\n",'Z');
    return 0;
}

Выход:

DIR:/xieerqi
skolodya@ubuntu:$ ./EOF                                                        
This is EOF: FFFFFFFF
This is Z: 5A

Еще одна любопытная вещь происходит со следующим кодом:

#include <stdio.h>
int main(void)
{
   char c;
   if (c = getchar())
    printf ("%x",c);
    return 0;
}

Если нажать Shift+ A, мы получим шестнадцатеричное значение 41, очевидно, такое же, как в таблице ASCII. Но для Ctrl+ Dмы ffffffffснова получаем возвращаемое значение, getchar()хранящееся в c.

DIR:/xieerqi
skolodya@ubuntu:$ gcc  EOF.c -o ASDF.asdf                                      

DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
A
41
DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
ffffffff

Обратитесь к другим языкам

Обратите внимание, что другие языки избегают этой путаницы, потому что они работают с оценкой состояния завершения функции, а не сравнивают ее с макросом. Как читать файл в Java?

    File inputFile  = new File (filename);
    Scanner readFile = new Scanner(inputFile);
    while (readFile.hasNext())
        { //more code bellow  }

Как насчет питона?

with open("/etc/passwd") as file:
     for line in file:
          print line
Сергей Колодяжный
источник
Отличный момент, действительно, персонаж отправляется как-то в какой-то момент.
Кос
Я думаю, что символ EOF - это то, что было потеряно при переводе, так как это не фактический символ, но EOT - это действительный символ ascii. Пойди разберись!
Сергей Колодяжный
1
Если вы попытаетесь прочитать /dev/null, это также должно вернуть EOF, верно? Или что я получу там?
Byte Commander
@ByteCommander позволяет узнать. Do cat / dev / null | кот -А.
Сергей Колодяжный
@ByteCommander добавил раздел, посвященный вашему комментарию
Сергей Колодяжный
2

EOF обозначает конец файла . Хотя я не знаю, как вызвать следующий символ, вы можете запустить следующую программу через конвейер файла, который в конце посылает сигнал EOF :

echo "Some sample text" | ./a.out

где a.outтвой скомпилированный источник

Паулюс Шукис
источник
1
Об этом уже говорилось, однако, с другой стороны, EOF - это не символ, я думаю, что неправильное представление проистекает из того факта, что сигнализируется посредством нажатия клавиши CTRL, который обычно является способом ввода непечатных символов. Как я понимаю, все, что на самом деле происходит, это то, что все входные данные сбрасываются, а read()возвращаемые данные 0, которые будут очищены пустыми (syscall), вернутся , что интерпретируется как EOF: stackoverflow.com/a/1516177/4316166
kos
@ kos, ты прав, это сигнал в конце концов.
Паулюс Шукис