Строка длиннее, чем ожидалось, и рассматривается как несколько входов

Это мой первый пост здесь, и я относительно новичок в C (это всего лишь мой второй блок на нем в универе).

По сути, я пытаюсь написать фрагмент кода, который спрашивает пользователя, хочет ли он продолжить. Если они пишут "да", код продолжает цикл. Если они пишут нет, код завершает программу. Если они пишут что-то еще, он просто снова спрашивает, пока не будет распознанного ввода. Я использую scanf и printf и пытаюсь использовать их только для создания этого кода.

char userInput[4];
userInput[0] = '\0';

while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
{
    printf("Do you want to continue (yes/no) :");
    scanf("%3s", userInput);
}

Я не включил остальную часть кода, просто для простоты.

Например, мой вклад

xxx

выход

Do you want to continue (yes/no) :

что хорошо Но тогда, если я введу:

xxxx

выход:

Do you want to continue (yes/no) :Do you want to continue (yes/no) :

и если я введу

xxxxxxx

я получил

Do you want to continue (yes/no) :Do you want to continue (yes/no) :Do you want to continue (yes/no) :

Кажется, что он почти сохраняет остальные символы после ожидаемой длины и сразу отправляет их на вход или что-то еще? Я хочу встроить защиту от слишком длинных струн, но, на мой взгляд, это не идеально.

Извините, если вопрос структурирован плохо, любая конструктивная критика приветствуется. Я не мог найти эту точную проблему нигде, поэтому я решил спросить себя.

3 ответа

Решение

Когда вы используете scanf("%3s", userInput) это будет только для чтения 3 символы завершены '\0' к userInput буфер. Однако, если вы введете больше, чем 3символы остальные все еще присутствуют во входном буфере в ожидании scanfчитать это. Вы можете опустошить свой буфер после каждого scanf избегая такого рода сюрпризов.

#include <stdio.h>
#include <string.h>
int main(void)
{
   char userInput[4];
   userInput[0] = '\0';
   int c;

   while ((strcmp(userInput, "yes") != 0) && (strcmp(userInput, "no") != 0))
   {
        printf("Do you want to continue (yes/no) :");
        scanf("%3s", userInput);

        while(1) // drain the input
        {
            c = getchar ();
            if(c=='\n') break;
            if(c==EOF)  return -1;
        }
    }      
    return 0;
}

Добро пожаловать в подводные камни для использования scanf для ввода пользователя. fgets или POSIX getline гораздо лучший выбор, потому что они избегают многих проблем ("Что осталось во входном буфере?"), присущих scanf, При этом важно знать, как использовать scanf должным образом.

С помощью scanf не что иное, как упражнение в учете (1) Какие символы были фактически прочитаны? и (2) Какие символы остаются непрочитанными во входном буфере? Чтобы обработать № 2, вам нужно каким-то образом очистить любые символы, которые остаются в буфере ввода между вызовами scanf чтобы вас не укусили оставшиеся. Вы также должны знать, что по крайней мере один символ остается, иначе вы просто заблокируете ожидание очистки ввода. Стандартный способ опорожнения stdin это просто перебрать любые символы, которые остаются, пока вы не прочитаете завершающий "\n' (результат нажатия пользователем "Enter") или EOF, например,

void empty_stdin (void)
{
    int c = getchar();
    while (c != '\n' && c != EOF)
        c = getchar();
}

(Вы также можете написать это как один for заявление, е, г. for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}, но пока, как правило, более читабельно.)

Это не просто вопрос правильной обработки ввода. Поскольку вы хотите заставить пользователя вводить "да" или "нет", общий подход заключается в непрерывном цикле, пока не будут выполнены критерии ввода. Что-то, с чем ты был на правильном пути, но не совсем понял. Вместо этого вы можете сделать:

char buffer[1024] = "";         /* don't skimp on buffer size */

for (;;) {                      /* loop continually */
    int rtn;                    /* value to hold return from scanf */
    printf ("Do you want to continue (yes/no): ");  /* prompt */
    rtn = scanf ("%1023[^\n]", buffer);             /* read input */
    if (rtn == EOF) {           /* user canceled input, bail */
        fprintf (stderr, "user canceled input.\n");
        exit (EXIT_FAILURE);
    }
    else if (rtn == 1) {        /* value stored in buffer */
        /* check for input meeting criteria */
        if (strcmp (buffer, "yes") == 0 || strcmp (buffer, "no") == 0) {
            empty_stdin();  /* don't leave characters in stdin */
            break;          /* success, break input loop */
        }
    }
    /* handle wrong input */
    empty_stdin();  /* don't leave characters in stdin */
    fprintf (stderr, "error: invalid input.\n\n");
}

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

Как упомянуто выше, правильное использование scanf - это просто проблема учета, когда вы знаете, как ведет себя каждый спецификатор формата, и влияние ошибок ввода или сопоставления останавливает чтение в точке сбоя, тогда вы можете ответить на вопросы (1) символы были на самом деле прочитаны? и (2) Какие символы остаются непрочитанными во входном буфере?

Краткий пример может помочь:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (void) {

    char buffer[1024] = "";         /* don't skimp on buffer size */

    for (;;) {                      /* process loop - doing stuff */
        printf ("\nprogram processing....\n\n");
        for (;;) {                      /* input - loop continually */
            int rtn;                    /* value to hold return from scanf */
            printf ("Do you want to continue (yes/no): ");  /* prompt */
            rtn = scanf ("%1023[^\n]", buffer);             /* read input */
            if (rtn == EOF) {           /* user canceled input, bail */
                fprintf (stderr, "user canceled input.\n");
                exit (EXIT_FAILURE);
            }
            else if (rtn == 1) {        /* value stored in buffer */
                /* check for input meeting criteria */
                if (strcmp (buffer, "yes") == 0) {
                    empty_stdin();  /* don't leave characters in stdin */
                    break;          /* success, break input loop */
                }
                else if (strcmp (buffer, "no") == 0)
                    goto alldone;
            }
            /* handle wrong input */
            empty_stdin();  /* don't leave characters in stdin */
            fprintf (stderr, "error: invalid input.\n\n");
        }
    }
    alldone:;

    printf ("that's all folks...\n");
}

(примечание: над "yes" а также "no" ответы сравниваются отдельно, чтобы обеспечить продолжение процесса "yes" или же exit на "no")

Пример использования / Вывод

$ ./bin/scanf_yes_no

program processing....

Do you want to continue (yes/no): what?
error: invalid input.

Do you want to continue (yes/no): obnoxious long line because cat stepped on kbd
error: invalid input.

Do you want to continue (yes/no): yes

program processing....

Do you want to continue (yes/no): yes

program processing....

Do you want to continue (yes/no):
error: invalid input.

Do you want to continue (yes/no): no
that's all folks...

Кажется, что он почти сохраняет остальные символы после ожидаемой длины и сразу отправляет их на вход или что-то еще?

Да, именно так и происходит.

Эти символы находятся в буфере ввода, ожидая, пока их не уничтожат, так же, как первые три символа были в один момент времени.

Вы отправляете символы с клавиатуры через операционную систему в терминал. Ваша программа, согласно тому, что вы написали, будет принимать текст до конца вселенной (хотя и по три символа за раз). Пока доступно больше символов, они будут поглощать их, и не имеет значения, как далеко распределялись нажатия клавиш во времени. C++ не может знать, что вы хотите, чтобы он делал что-то еще.

При этом, изменяя свою семантику (как показано в других ответах), можно получить более интуитивное поведение для запроса вызова / ответа, подобного этому.

Другие вопросы по тегам