C ++ cin.fail () выполняется и перемещается на следующую строку, даже если ввод имеет другой тип данных - PullRequest
1 голос
/ 15 мая 2019

Я использую get.fail (), чтобы проверить, есть ли на входе какой-либо символ, и, если он есть, я бы хотел дать пользователю возможность повторно войти.Тем не менее, программа, кажется, все еще принимает пользовательский ввод всякий раз, когда перед вводом находится целое число, независимо от случая.Скажем w1 и 1w, программа сообщит пользователю, что она принимает только целые числа, а последняя принимает входные данные и переходит на следующую строку, что затем вызывает другую проблему.

    void userChoice(int input){

        switch(input)
        {
            case 1:
                insert();
                break;
            case 2:
                display();  
                break;
            case 3:
                update_data();
                break;
            case 4:
                delete_position();
                break;
            case 5:
                cout<<"Thank you for using the program\n";
                exit(0);
                break;
            case 6:
                tellSize();
                break;
            default:
                cout<<"Not an option\n";
                cin>>input;

                while(cin.fail())
                {
                    cin.clear();
                    cin.ignore(INT_MAX, '\n'); 
                    cin>>input;
                    break; 
                }
                userChoice(input);
        }

    }

Ссылаясь на код выше, скажем, я даю ввод 1w.Программа все равно выполнит случай 1, как будто в этом нет ничего плохого, и затем w каким-то образом передается в функцию insert(), что не то, что я хочу.Я хотел бы, чтобы программа позволяла пользователю повторно вводить ввод независимо от того, является ли он 1w или w1, короче говоря, я не хочу, чтобы программа переместилась на следующую строку, если в целочисленном вводе есть символ.

tl; dr: Почему приведенный ниже код по-прежнему выполняется, когда cin равен 1w, не должен ли он печатать «Введите только номер», поскольку там есть символ?

Редактировать:Вот небольшая программа, которую я сделал, чтобы воспроизвести ошибку, с которой я сталкиваюсь, я сначала ввожу 1h и вот первая ошибка, с которой я сталкиваюсь, почему программа все еще выполняется, когда на входе есть символ h?Затем на втором входе я ввожу 2w, и программа выводит 2, не должна ли программа зациклить цикл while, поскольку на входе есть символ w?

#include<iostream>
#include<iomanip>
#include<limits>
using namespace std;

void option_1()
{
    int amount_input;

    cout<<"Enter the amount of cake"<<endl;
    cin>>amount_input;
    while(cin.fail())
    {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n'); 
        cout<<"Enter number only\n";
        cin>>amount_input;
    }
        cout<<amount_input;
}

void options(int input)
{
    bool fail;

    switch(input)
    {
        case 1:
            option_1();
            break;


        default:
            cout<<"Not an option"<<endl;
            cin>>input;
            while(cin.fail())
            {
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(), '\n'); 
                cin>>input;
                break; 
            }
            options(input);

    }
}

void menu(){
    int user_input;

    cout<<"Enter 1 to print something\n";
    cin>>user_input;
    options(user_input);
}

int main(){
    menu();
}

1 Ответ

1 голос
/ 15 мая 2019

Из ваших комментариев кажется, что вы хотите целочисленный ввод исключительно и не хотите разрешать ввод дополнительного символа после целого числа, такого как 1w, даже если 1 будет преобразовано в int оставляя w непрочитанным, чтобы быть удаленным .ignore(...) после вашего звонка на .clear().(как уже упоминалось выше, вы используете .clear() и .ignore(...) теперь правильно.

Если это ваше намерение, вам нужен способ проверить, есть ли что-нибудь еще после целочисленного ввода пользователем.Чтобы сделать это неблокирующим способом, если на самом деле ничего не существует, у вас есть несколько вариантов (например, вы можете .peek() на следующем символе - но вы получите только один, или вы можете использовать line-ориентированный подход ввода) линейный подход позволяет вам читать всю строку ввода пользователя в string, а затем извлекать целочисленное значение и проверять, есть ли в строке что-либо еще

Самый простой способ - создать std :: basic_stringstream из строки данных, считанной с getline(). Этот подход использует всю строку пользовательского ввода ипредоставляет вам все инструменты, необходимые для извлечения любой необходимой вам информации из линии, а также делает это таким образом, чтобы не влиять ни на один из ваших последующих пользовательских вводов.

Хотя я бы порекомендовал вам объединить функции void menu() и void options(int input), чтобы у вас просто была одна функция для обработки ввода для вашего меню - нет ничего плохого в том, чтобы разбить ее на две части, кроме возможностинесколько строк кода дублируются.Ниже приведено лишь предложение о том, как обрабатывать вашу функцию menu(), чтобы разрешить только целочисленный ввод.Вы можете адаптировать его к остальной части вашего кода.

Вам понадобится несколько дополнительных включений:

#include <sstream>
#include <string>

Я бы также #define первый и последний приемлемые пункты меню, чтобы выэти константы должны быть доступны в вашем коде в месте, которое можно легко изменить при добавлении в меню, например,

#define MENUFIRST 1     /* first valid entry */
#define MENULAST  1     /* last valid entry */

( примечание: , что позволит вводить только 1в качестве действительного пункта меню)

Чтобы ограничить вашу функцию menu() с использованием подхода, описанного выше, вы можете сделать:

void menu(){

    int user_input = 0;
    string line, unwanted;

    for (;;) {  /* loop continually until valid input received */
        cout << "\nEnter 1 to print something: ";
        if (!getline (cin, line)) { /* read an entire line at a time */
            cerr << "(user canceled or unrecoverable stream error)\n";
            return;
        }
        stringstream ss (line);         /* create a stringstream from line */
        if (!(ss >> user_input)) {      /* test if valid integer read */
            /* test eof() or bad() */
            if (ss.eof() || ss.bad())   /* if not, did user cancel or err */
                cerr << "(empty-input or unreconverable error)\n";
            else if (ss.fail())         /* if failbit - wasn't an int */
                cerr << "error: invalid integer input.\n";
        }
        else if (ss >> unwanted) {      /* are there unwanted chars? */
            cerr << "error: additional characters following user_input.\n";
            user_input = 0;             /* reset user_input zero */
        }       /* was int outside MENUFIRST-to-MENULAST? */
        else if (user_input < MENUFIRST || MENULAST < user_input)
            cerr << "error: integer not a valid menu selection.\n";
        else    /* valid input!, break read loop */
            break;
    }

    options(user_input);
}

Комментарии должны быть самоочевидными, учитывая обсуждение выше,но дайте мне знать, если у вас есть вопросы.Используя функцию с остальным кодом (и комментируя неиспользованный // bool fail;), вы можете проверить, соответствует ли он вашим требованиям, например,

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

$ ./bin/menu_int

Enter 1 to print something: w1
error: invalid integer input.

Enter 1 to print something: $#%&^#&$ (cat steps on keyboard) !#$%%^%*()
error: invalid integer input.

Enter 1 to print something: 1w
error: additional characters following user_input.

Enter 1 to print something: -1
error: integer not a valid menu selection.

Enter 1 to print something: 24
error: integer not a valid menu selection.

Enter 1 to print something: 1
Enter the amount of cake
3
3

Также обратите внимание, что ваша menu() функция теперь будет правильно перехватывать руководство EOF, сгенерированное пользователем, нажимающим Ctrl + d (или Ctrl + z на окнах) дляотменить ввод и выйти изящно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...