C++ cin.fail() 执行并移动到下一行,即使输入是另一种数据类型

C++ cin.fail() executes and moves to next line even though input is of another data type

我正在使用 get.fail() 检查输入中是否有任何字符,如果有我想给用户重新输入的机会。但是,无论如何,只要输入前面有一个整数,程序似乎仍然会接受用户输入。说 w11w,程序会告诉用户它只接受整数,而后者接受输入并移到下一行,这会导致另一个问题。

    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;博士: 为什么下面的代码在 cin 为 1w 时仍然执行,它不应该打印 "Enter number only" 因为那里有一个字符吗?

编辑:这是我制作的一个快速程序,为了重现我面临的错误,我首先输入 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();
}

根据您的评论,您似乎只希望 整数输入 ,并且不想允许在整数后输入其他字符,例如 1w,即使 1 将被转换为 int,同时留下 w 未读,以便在您调用 .clear() 后被 .ignore(...) 删除。 (如上所述,您现在对 .clear().ignore(...) 的使用是正确的。

如果这是您的意图,您需要一种方法来检查用户输入的整数后是否还有其他内容。如果实际上什么都不存在,要以 non-blocking 的方式做到这一点,您有几个选择。 (例如,您可以 .peek() 在下一个字符处——但您只能得到一个,或者您可以使用 line-oriented 输入方法) line-oriented 方法允许您将整行用户输入读入 string,然后提取整数值并检查输入行中是否包含任何其他内容。

最直接的方法是从用 getline() 读取的数据行创建一个 std::basic_stringstream。这种方法消耗了整行用户输入,并为您提供了从该行中提取您可能需要的任何信息所需的所有工具。它还以不影响任何后续用户输入的方式执行此操作。

虽然我建议您将 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);
}

根据上面的讨论,评论应该是 self-explanatory,但如果您有任何问题,请告诉我。将函数与其余代码一起使用(并注释未使用的// bool fail;),您可以测试它是否满足您的要求,例如

例子Use/Output

$ ./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() 函数现在将正确地捕获由用户按 Ctrl+d(或 Ctrl+z on windows) 取消输入并正常退出。