Перейти к содержимому

Eof си как ввести

  • автор:

Си: проблема с getchar() и EOF(^Z) в Windows консоли

Уже очень долгое время пытаюсь понять: Почему цикл не завершается, если я введу «dfkjsdf^Z», в то время как при
«dfkjsdf (тут я нажимаю Enter) ^Z» — завершается? То есть, как сделать так, чтобы он вышел из цикла, если я нажимаю CTRL+Z до того, как нажму Enter Это, пожалуй, самое непонятное для меня в языке Си. И сколько бы я не рылся в гугле, ответа все же найти не смог..

int main() < int c; int i = 0; int arr[10]; while((arr[i] = getchar()) != EOF && i < 10) < printf("arr[i] is %c\n",arr[i]); i++; >return 0; > 

Отслеживать
52.3k 11 11 золотых знаков 108 108 серебряных знаков 312 312 бронзовых знаков
задан 15 дек 2017 в 20:20
83 7 7 бронзовых знаков

То есть вы набираете на клавиатуре dfkjsdf^Z без Enter’а? И да, какая у вас система, и какая консоль? Обработка ^Z зависит от консоли.

15 дек 2017 в 20:32
Это поведение не языка Си, а виндоус консоли (переходите в *nix)
15 дек 2017 в 20:42
Да. То есть я ввожу dfkjsdf , затем CTRL+Z , затем Enter . Windows 10. cmd
15 дек 2017 в 20:58

@avp: Чем тут должен помочь «переход в *nix»? Логика Ctrl-D в *nix отличается в деталях, но внешне она похожа. При построчной буферизации конец файла возникает только при нажатии Ctrl-D на пустом буфере, то есть нажимать Enter перед Ctrl-D придется и там.

16 дек 2017 в 15:42
@AnT, в родном для языка окружении проще учиться (и вообще . )
16 дек 2017 в 16:50

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Это не имеет никакого отношения к языку С, а зависит только от алгорима обработки комбинации Ctrl + Z консолью Windows и интерпретацией результатов этой обработки той реализацией стандартной библиотеки, которую вы используете.

Ввод в Windows терминале буферизуется построчно. При этом обработка присутствующих в буфере символов ^Z следует довольно запутанному алгоритму (по крайней мере при использовании стантартной библиотеки из комплекта MSVC).

  • Комбинация Ctrl + Z сама по себе не вызывает «проталкивания» накопленного буфера на выход (в отличие от комбинации Ctrl + D в Linux). Она лишь добавляет во входной буфер символ ^Z , т.е. \x1a . Вы можете нажать Ctrl + Z несколько раз, помещая во входной буфер несколько символов ^Z . После этого вы можете продолжать вводить что-то еще. Чтобы все-таки послать накопленный буфер ожидающему процессу, придется нажать Enter .
  • Если входной буфер содержит какие-то символы до первого появления символа ^Z , то ожидающий ввода процесс увидит все эти символы, после чего процесс увидит один символ ^Z , т.е. \x1a . Это будет просто символ \x1a . Никакой ситуации «конец файла» при этом не возникнет. Однако остаток входного буфера (после первого символа ^Z ) процессу виден не будет, как будто его и не было. То есть если вы введете в Windows терминале последовательность abc^Z^Zdef^Zghi и нажмете Enter , то на вход ваш процесс получит символы a , b , c и \x1a . Весь остальной ввод пропадет бесследно. Заметьте, что при этом «пропадает» и символ перевода строки, cгенерированный нажатием Enter .
  • Если входной буфер сразу же начинается с символа ^Z , то входной буфер считается пустым. Все его содержимое пропадает, не происходит даже чтения символа ^Z . Возникает ситуация «конец файла». То есть если вы введете в Windows терминале последовательность ^Zdef и нажмете Enter , то на вход ваш процесс не получит вообще ничего. Вместо этого функция ввода сообщит вам, что наткнулась на конец файла.

Поэтому для того, чтобы создать в буферизованном консольном вводе ситуацию «конец файла» придется вводить ^Z в самом начале новой строки.

Если вам нужна посимвольная обработка входа, то можно предварительно отключить построчную буферизацию ввода

HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); DWORD dwMode; GetConsoleMode(hIn, &dwMode); dwMode &= ~ENABLE_LINE_INPUT; SetConsoleMode(hIn, dwMode); 

В таком варианте каждый введенный символ будет немедленно читаться вашей getchar() и символ ^Z будет немедленно интерпретироваться как конец файла.

Условие !=EOF, что надо сделать чтобы оно было истинным

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

6 ответов

15 мая 2007 года
1.4K / / 24.07.2006

EOF — это End Of File. Поэтому юзать этот макрос надо при чтении файла, чего я у тебя не заметил. К тому же у тебя не определено значение nc, а ты его инкрементируешь. Напиши
int nc=0;
Если ты хочешь получить с клавиатуры значение, по которому можно из цикла выйти, то и впиши то, что с клавиатуры ввести можно. Например
while ((getchar())!=’e’)
Должно выполняться, пока не брякнешь на e

15 мая 2007 года
10 / / 12.05.2007

~ArchimeD~
спасибо за разъяснения
Почему же в книжке приводятся эти примеры, которые работают неправильно?
По поводу, любого символа с клавиатуры я догадался, было непонимание по поповоду EOF. Спасибо:)

15 мая 2007 года
1.4K / / 24.07.2006

Я так думаю че-то вроде какого-нибудь наброска. Типа как работает цикл. Или, че получится, если перенаправить ввод. А че за книжица то?

15 мая 2007 года
1.0K / / 08.01.2007

Так вообще не рекомендуется делать,есть такое правило «Всегда инициализируйте переменные».См.например Г.Саттер А.Александреску «Стандарты программирования на С++»,правило 19.А
в твоем коде — тем более.

Неинициализированные перемнные — распространенный источник ошибок в программах на С и С++.Избегайте их выработав привычку очищать память перед ее использованием , инициализируйте переменные при их определении.

// Не рекомендуется — не инициализирует переменную
int speedFactor;
if(condition)
speedFactor = 2;
else
speedFactor = -1;
// Лучше:инициализирует переменную
int speedFactor = -1;
if(condition)
speedFactor = 2;
// Еще лучше
int speedFactor = condition ? 2 : 1;

int eof(int fd)

Функция eof() является частью UNIX-подобной системы и не определена в стандарте ANSI С. При вызове с действительным дескриптором файла eof() возвращает 1, если был достигнут конец файла; в противном случае она возвращает 0. В случае ошибки она возвращает —1, a errno уста­навливается в EBADF (плохой номер файла).

Следующая программа выводит на консоль текстовый файл , используя eof ( ) , чтобы определить момент достижения конца файла.
#include
#include
#include
#include
int main ( int argc , char * argv [ ] )
{
int fd ;
char ch ;
if ( ( fd = open ( argv [ 1 ] , O_RDWR ) ) ==- 1 ) {
printf ( «Cannot open file. \n » ) ;
exit ( 1 ) ;
}
while ( ! eof ( fd ) ) {
read ( fd , & ch , 1 ) ; /* чтение по символу за раз */
printf ( «%с» , ch ) ;
}
close ( fd ) ;
return 0 ;
}

Научный форум dxdy

Вот занялся нормальным изучением языка си, естественно воспользовался классикой Керниган, Ричи. Трудность понимания возникла в начале при чтении главы про «символическую константу EOF». Там говорят, что она обозначает конец входного потока ну и приведен текст программы где она используется:

Используется синтаксис C
while ( ( c = getchar ( ) ) != EOF )
putchar ( c ) ;

Так вот, здесь я так понимаю входной поток — это данные(символы), введенные с клавиатуры .Тогда что принимать за конец входного потока? Я же могу бесконечно набирать текст с клавиатуры (насколько позволяет память), как функция getch() определит эту константу и когда?

Re: Смысл EOF в си
14.07.2012, 21:45

Я тоже недавно читал/компилял. В винде программа выдаёт результат после нажатия ctrl+c, т.е. после прерывания её работы.

Re: Смысл EOF в си
14.07.2012, 23:04

А, то есть нажатием комбинации клавиш ctrl+c мы передаем системе значение EOF. Правильно? Ну пока как-то не особо ясно для чего нужна эта константа. Там даже было задание: написать программу, выводящую значение EOF, я написал, скомпилил, в результате вывелся пробел.

Re: Смысл EOF в си
14.07.2012, 23:18

Админ форума

Dosaev в сообщении #595304 писал(а):
[syntax lang=»c»]#include
.
[/syntax]

Dosaev , при включении кода в текст сообщения используется что-нибудь одно: или [cоde], или [syntаx]. Лучше [syntаx], причем для облегчения последующих ссылок на код — с атрибутом lines=n (для нумерации строк). Поправил.

Re: Смысл EOF в си
14.07.2012, 23:18

Заслуженный участник

Вообще-то EOF вводится по Ctrl+D (UNIX) / Ctrl+Z (Win). Ctrl+C — это прерывание работы программы.

Зачем эта константа нужна? Чтобы отследить, когда поток ввода закончится. Так всегда было: чтобы ввести простенький текст в файл с консоли, достаточно скомандовать «copy con 1.txt», ввести текст и нажать Ctrl+Z.

Re: Смысл EOF в си
14.07.2012, 23:19

Последний раз редактировалось Pavia 14.07.2012, 23:21, всего редактировалось 1 раз.

Цитата:

Тогда что принимать за конец входного потока? Я же могу бесконечно набирать текст с клавиатуры (насколько позволяет память), как функция getch() определит эту константу и когда?

Вот что написано в википедии.

Цитата:

Для указания терминалу в UNIX «EOF» следует воспользоваться комбинацией клавиш Ctrl+D. В Windows — Ctrl+Z.

На самом деле всё сложнее. EOF не является символом.
getch() По примеру большинства функций возвращает -1 когда происходит внутренняя ошибка. Такой ошибкой является конец файла. Закрытие файла.

Всё является файлом. Так вот консольный/терминальный ввод и вывод обычно на более низком уровне представим как файлы с хэндалами 0 и 1.
getch попросту читает данные из файла и как только обнаруживает что файл закрыт то выдает код ошибки -1=EOF.

Техническая реализация зависит от ОС. Но скорее всего ОС является POSIX совместимой.

Представление ввода вывода в виде файлов удобно для реализации перенаправления ввода/вывода. К примеру в дос можно написать в терминале «test.exe По идеи если написать «test.exe

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *