Страницы

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

воскресенье, 15 декабря 2019 г.

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

#c #windows #console #io


Уже очень долгое время пытаюсь понять: 

Почему цикл не завершается, если я введу "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;
}

    


Ответы

Ответ 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 будет немедленно интерпретироваться как конец файла.

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

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