Страницы

Поиск по вопросам

пятница, 26 октября 2018 г.

Си: проблема с 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
",arr[i]); i++; } return 0; }


Ответ

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

Комментариев нет:

Отправить комментарий