在使用 cin 请求 int 时如何有效地防止用户输入?

How do I efficiently foolproof user input while using cin to ask for an int?

这是我第一次 post 在这里,我对 programming/C++ 还很陌生(实际上只是进入这个兔子洞几个星期)。我在 Microsoft Visual Studio 2017 中有这个测试项目,我想弄清楚如何在使用 cin 时完全防止用户输入。如果我要求一个 int,而我只想要一个 1 或一个 0,我希望有人绝对没有办法在一个不正确的数字之间放置一个像 2、n 或 space 这样的东西以及 "n 1" 之类的正确响应。目前,我已经到了这样的地步,我似乎可以创建一个不需要的结果的唯一方法是,如果我首先输入一个正确的整数(0 或 1),然后用 space 和任何其他字符跟进,并且这种模式可以在初始正确的(0 a 42 f 9130)等之后跟不定数量的 space 和字符等。除了用更混乱的代码获得所需的结果之外,我想知道如果我只是缺少一些我还没有听说过的内置功能,这些功能可以使这个过程更有效率。这是我为达到这一点而写的内容:

#include <iostream>
#include <string>
#include <climits>

using namespace std;

int trap;
int wrongNumber();

int numOnly() {
    while (cin.fail())
    {
        // Using someone else's code for this while statement to figure out how to not take a char input when using an int
        // Update: Turned this into a function to be called on whenever cin has a chance to fail because users don't listen.

        cin.clear(); // clear input buffer to restore cin to a usable state
        cin.ignore(INT_MAX, '\n'); // ignore last input
        system("CLS");
        cout << "----------------------------------" << endl;
        cout << "|  You can only enter a number.  |" << endl;
        cout << "| Would you like to pick a card? |" << endl;
        cout << "|  Type 1 for yes or 0 for no!   |" << endl;
        cout << "----------------------------------" << endl;
        cin >> trap;
    }
    if (trap != 1 && trap != 0) {
        system("CLS");
        wrongNumber();
    }
    return trap;
}

int wrongNumber() {

    // At least I made this fail-safe on my own!

    while (trap != 1 && trap != 0) {
        system("CLS");
        cout << "----------------------------------" << endl;
        cout << "|    That is not a 1 or a 0!     |" << endl;
        cout << "| Would you like to pick a card? |" << endl;
        cout << "|   Type 1 for yes or 0 for no!  |" << endl;
        cout << "----------------------------------" << endl;
        cin >> trap;
    }
    if (cin.fail()) {
        system("CLS");
        numOnly();
    }
    return trap;
}

int main() {

    cout << "----------------------------------" << endl;
    cout << "| Would you like to pick a card? |" << endl;
    cout << "|   Type 1 for yes or 0 for no!  |" << endl;
    cout << "----------------------------------" << endl;
    cin >> trap;

    while (cin.fail())
    {
        numOnly();
    }

    if (trap != 1 && trap != 0) {
        wrongNumber();
    }

不依赖某些 OS 函数的最佳方法:

#include <iostream>
#include <limits> // std::numeric_limits
#include <cctype> // std::isspace

// function to read and discard whitespace except '\n' from a stream
std::istream& eat_whitespace(std::istream &is)
{
    char ch;

    // as long as the next character in the stream is a space and not a newline
    while (std::isspace(ch = is.peek()) && ch != '\n') 
        is.get(); // get and discard the character

    return is;
}

int read_range_strict(std::istream &is, int min, int max)
{
    int value;

    // as long as
    while (!(is >> value >> eat_whitespace) // extraction of an int fails
           || is.peek() != '\n' // or the next character in the stream is not a newline *)
           || value < min || max < value // or the value is not within the specified range
    ) {

        std::cerr << "Please enter a number between " << min << " and " << max << "!\n\n";
        is.clear(); // clear flags
        // discard everything that might be left in the stream
        is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }

    return value;
}

int main()
{
    int value;
    do {
        value = read_range_strict(std::cin, 0, 1);
    } while (true); // only for demo
}

*) 我们尝试使用 eat_whitespace 建立。因此,如果流中还有任何不是 \n 的内容,我们会在数字后得到一些垃圾。

由于唯一的有效值是 0 和 1,因此您不需要将输入读取为 int。只需将其作为字符串读取,trim 任何空格并将您的字符串与“0”或“1”进行比较。

当然你也可以只接受 "y" 或 "n" 而不是这样会更加用户友好。

正如其中一个答案所述,将输入读入字符串然后计算该字符串更容易。这是一个例子:

#include <iostream>
#include <string>

int main() {
  std::string trap;

  std::cout << "Enter 1 or 0" << std::endl;
  std::getline(std::cin, trap); // fetch user input, save into trap
  while (std::cin.fail() || (trap != "1" && trap != "0")) {
    std::cout << "That was not a 1 or 0; try again" << std::endl;
    std::getline(std::cin, trap);
  }
  return 0;
}

此代码读取所有用户输入,确定它是 1 还是 0,然后成功退出或根据用户输入的内容提示用户。

如果我没理解错的话,这样的事情可能会帮助您在万无一失的用户输入方面实现您想要的效果。我建议您将代码调整为类似的东西,而不是让多个函数执行类似的任务。

根据您的编译器,您可以使用 getch() 之类的 C 代码,并在完成检查后才将其回显到屏幕上。然后你必须一个字符一个字符地获取代码,显然 assemble 你的字符串。

https://www.c-lang.thiyagaraaj.com/archive/c-blog/use-of-getch-getche-and-getchar-in-c

免责声明:并非所有 C++ 编译器都支持此功能。这是 C 代码而不是 C++。

我建议您不要使用整数来存储 "Yes" 或 "No" 答案,而是使用字符串。通过这种方式,您可以使用 cin.fail()cin.ignore()cin.clear() 为自己节省一些代码行:

 int main() {

    string trap;
    cout << "----------------------------------" << endl;
    cout << "| Would you like to pick a card? |" << endl;
    cout << "|   Type 1 for yes or 0 for no!  |" << endl;
    cout << "----------------------------------" << endl;
        cin>>trap;

        while (trap != "1" && trap != "0") { //You can make this while block into a function if you prefer
            cout << "----------------------------------" << endl;
            cout << "|    That is not a 1 or a 0!     |" << endl;
            cout << "| Would you like to pick a card? |" << endl;
            cout << "|   Type 1 for yes or 0 for no!  |" << endl;
            cout << "----------------------------------" << endl;
                cin>>trap;
        }
    return 0;
}

如果您必须使用整数,那么您应该查看this and capturing characters without pressing enter