getchar не останавливается при использовании scanf

Мне трудно понять getchar(). В следующей программе getchar работает как положено:

#include <stdio.h>


int main()
{
    printf("Type Enter to continue...");
    getchar();
    return 0; 
} 

Однако в следующей программе getchar не создает задержки, и программа завершается:

#include <stdio.h>

int main()
{
    char command[100];
    scanf("%s", command );
    printf("Type Enter to continue...");
    getchar();
    return 0; 
} 

У меня есть следующий обходной путь, который работает, но я не понимаю, почему:

#include <stdio.h>

int main()
{
    char command[100];
    int i;
    scanf("%s", command );
    printf("Type Enter to continue...");
    while ( getchar() != '\n') {
      i=0; 
    }
    getchar();
    return 0;    
}

Итак, мои вопросы:
1. Что делает scanf? Почему scanf делает это?
2. Почему моя работа работает?
3. Как можно эмулировать следующий код Python:

raw_input("Type Enter to continue")

person oz123    schedule 29.09.2012    source источник


Ответы (5)


Ввод отправляется в программу только после ввода новой строки, но

scanf("%s", command );

оставляет новую строку во входном буфере, поскольку формат %s(1) останавливается, когда первый символ пробела встречается после некоторого непробельного символа, getchar() затем немедленно возвращает эту новую строку и не нужно ждать дальнейший ввод.

Ваш обходной путь работает, потому что он очищает новую строку из входного буфера перед повторным вызовом getchar().

Чтобы эмулировать поведение, очистите буфер ввода перед печатью сообщения,

scanf("%s", command);
int c;
do {
    c = getchar();
}while(c != '\n' && c != EOF);
if (c == EOF) {
    // input stream ended, do something about it, exit perhaps
} else {
    printf("Type Enter to continue\n");
    getchar();
}

(1) Обратите внимание, что использование %s в scanf очень небезопасно, вы должны ограничить ввод тем, что может хранить ваш буфер с шириной поля, scanf("%99s", command) будет считывать не более 99 (sizeof(command) - 1)) символов в command, оставляя место для 0-терминатора.

person Daniel Fischer    schedule 29.09.2012

Пробел является разделителем для описателя формата 5y3 %s, а новая строка рассматривается как пробел, поэтому он остается в буфере. Консольный ввод обычно ориентирован на строку, поэтому последующий вызов getchar() немедленно вернется, потому что «строка» остается в буфере.

scanf("%s", command );
while( getchar() != '\n' ){ /* flush to end of input line */ }

Точно так же, если вы используете getchar() или %c для получения одного символа, вам обычно нужно очистить строку, но в этом случае введенный символ может сам быть новой строкой, поэтому вам нужно немного другое решение:

scanf("%c", ch );
while( ch != '\n' && getchar() != '\n' ){ /* flush to end of input line */ }

аналогично для getchar():

ch = getchar();
while( ch != '\n' && getchar() != '\n' ){ /* flush to end of input line */ }

Разумно, конечно, сделать эти решения в автономных специализированных функциях ввода, которые вы можете использовать повторно, а также использовать в качестве места для размещения общего кода проверки ввода и проверки ошибок (как в ответе Даниэля Фишера, который разумно проверяет EOF - обычно вы хотите избежать дублирования этих проверок и обработки ошибок везде).

person Clifford    schedule 29.09.2012

Я бы предпочел сначала использовать fgets, а затем использовать sscanf для анализа ввода. Я делал подобные вещи в течение долгого времени, и поведение было более предсказуемым, чем при использовании простого scanf.

person Pablo    schedule 29.09.2012

Ну, у меня проще: добавить еще getchar()... проблема решена!!

person Girinath Ravichandran    schedule 20.09.2014
comment
это не обязательно решит проблему, у вас все еще может быть более одного байта в буфере, и getchar() немедленно вернется. - person Pablo; 25.09.2014

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

fflush(stdin);

но очистка входного потока приводит к неопределенному поведению (хотя библиотека Microsoft C определяет поведение как расширение).

person Debobroto Das    schedule 29.09.2012
comment
fflush() не определен для входных потоков. - person Clifford; 29.09.2012
comment
-1: применение fflush() к входному потоку (или входному/выходному потоку, для которого была введена последняя операция) является одним из немногих конкретных примеров неопределенного поведения, указанных в Стандарт C. - person pmg; 29.09.2012
comment
Обратите внимание, что хотя это работает с библиотекой Microsoft C, это определенно не работает с библиотекой GNU C. Избегать. - person Clifford; 29.09.2012
comment
Я упомянул о неопределенном поведении. - person Debobroto Das; 29.09.2012
comment
@DebobrotoDas: вы отредактировали ниндзя свой ответ, и отрицательные голоса (по крайней мере, мои) не могут быть отозваны. Я отредактирую ваш ответ и отменю свой отрицательный голос через секунду. - person pmg; 29.09.2012
comment
Это я проголосовал за ваш ответ. Я сделал это до того, как в ответе появилась информация о Undefined Behavior. Когда я заметил, что вы изменили его, я не смог отозвать отрицательный голос, но отозвал его сразу после того, как отредактировал ваш ответ. Извините за путаницу. - person pmg; 29.09.2012
comment
ИМО остается плохим советом - undefined включает в себя вообще ничего не делать. Это может заслуживать или не заслуживать отрицательного голоса, но уж точно не положительного. - person Clifford; 29.09.2012
comment
fflush(stdin); не определено и не является хорошей привычкой. Я рекомендую сначала использовать fgets, а затем sscanf. Это намного безопаснее и предсказуемее. - person Pablo; 29.09.2012