【问题标题】:How To Check If User's Input Is Valid? (C++)如何检查用户输入是否有效? (C++)
【发布时间】:2014-08-08 05:07:46
【问题描述】:

在基于一组指令创建程序时,我在设计伪代码和实现实际代码方面做得很好。我觉得我缺乏的是检查用户的输入(无论是有效还是无效)。在练习编程时,我创建了自己的方法来检查验证用户的输入。但是代码很长,我觉得不够(我会解释原因)。我想知道是否有更好的方法来检查用户的输入。以及其他程序员如何实现他们的代码。

这就是我验证用户输入的方式:

if(cin.fail()) {
        cout << "Invalid Input" << endl;
        cout << "Now Exiting..." << endl;
        return;
}
// I didn't know how to skip a line while in code
while(input < 0) {
        cout << "Invalid Input" << endl;
        cout << "Enter radius: " << endl;
        cin >> input;
        if(cin.fail()) {
             cout << "Error: Invalid Input" << endl;
             cout << "Now Exiting..." << endl;
             return;
        }
}

当 cin 无法将值单独存储到变量中时(第 1 - 5 行,第 11 -15 行)我退出的原因是因为如果我将 cin.fail() 添加到 while 条件并尝试输入一个字母,它开始一个无限循环。我做了一些研究,发现你必须先 cin.sync(),然后是 cin.clear()。但我仍然得到无限循环。

代码如下:

do {
        cin.sync()
        cin.clear();
        cout << "Enter radius: ";
        cin >> input;
} while(input < 0 || cin.fail());

如果我做错了什么,看看更好的方法来验证用户的输入会很有帮助。

【问题讨论】:

  • 只需使用while (getline(cin, somestring))。它会让您的生活更轻松。
  • 更一般地,阅读 C++ 中的错误处理。使用 try、catch 和 throw 真的会帮助你。
  • 如果您想进行真正强大的验证,请使用一些正则表达式。从 C++11 开始,它是 STL 的一部分。允许您说“密码必须是 12 个字符,一个大写字母和一个特殊字符”之类的内容,然后只需几行即可检查。正则表达式并不像看起来那么难,非常值得学习。

标签: c++ validation user-input infinite-loop cin


【解决方案1】:

我不推荐使用 std::cin,因为它会在输入缓冲区中第一个找到的空白实例之后留下所有剩余的用户输入。这将产生问题,除非您使用 cin.ignore() 删除剩余的字符。通常认为使用 getline() 是更好的做法,它将所有字符都放在换行符之前。如果您确实选择使用 std::cin,则需要使用 cin.ignore() 删除剩余的字符,并使用 cin.clear() 重置 cin 的失败位,以便 while 条件在下一次通过循环。

以下是我将如何解决问题。它使用 getline() 获取所有字符,并使用 stringstream 将字符串转换为 int。请注意,您需要像使用 cin 一样清除字符串流的失败位,以确保当您执行 ss >> 导致 while 条件时条件正常工作。

std::cout << "Enter radius: ";
getline(std::cin, input);
std::stringstream ss(input);

while(!(ss >> result)) {
    std::cout << "Invalid Input" << std::endl;
    std::cout << "Enter radius: ";
    getline(std::cin, input);
    ss.clear();
    ss << input;
}

下面我还将包含一些使用 std:cin 解决问题的代码。不过,我仍然建议使用 getline()。注意: std::numeric_limits::max() 用于指定要从输入缓冲区中删除多少字符。使用它而不是您自己的任意数字是更好的做法,因为您无法确定用户将输入多少个字符。 cin.ignore() 将删除所有字符直到给定数字或直到它到达作为其第二个参数提供的字符的实例,在这种情况下是换行符 ('\n')。

std::cout << "Enter radius: ";
std::cin >> result;

while(std::cin.fail()) {
    std::cout << "Invalid Input" << std::endl;
    std::cout << "Enter radius: ";
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin >> result;
}

【讨论】:

  • 第一个解决方案只有在第二个输入有效时才有效。如果第二个输入无效,则它会留在流ss 中,因为ss.clear() 只重置状态标志,而不是底层字符串。这会导致提取到result 总是失败,并以无限循环结束。例如,一种完全重置流的方法是std::stringstream temp(input); ss.swap(temp)
【解决方案2】:

输入验证问题是一种简单的解析形式。

有一些语言类(在形式语言理论领域)可以表达您输入的复杂性。这些类被称为常规的、上下文无关的和图灵完备的。

您必须考虑您的程序可能接收的所有可能输入,并决定您的程序是否应该接受它们。语言类可帮助您决定需要哪种输入验证。

如果语言是正则的(在你的情况下)你可以使用正则表达式来验证输入。

例如,无上下文语言是数学公式。您无法使用正则表达式计算括号的数量。因此,无法使用正则表达式检查 ((a+b) * (c+d)) 的括号数量是否正确。

到目前为止,这些都是关于当您更自然地编程时应该做什么的提示。

为了简单起见,请做一个非常受限制的正则表达式,例如手动解析。 你真正想用伪代码做什么:

do {
   std::cout << "Please enter radius: ";

   line = read_a_line_from(std::cin) // separated by '\n' the newline

   if (false == really_read_a_line(line)) {
      /* error handling for std::cin, dealing with i.e.: the fail bit */
      break; /* exit the loop */
   }
   if (line == "exit") { // give the user an explicit exit, to quit gracefully
      exit(SUCCESS); /* exit the program */
   }

   if (false == is_a_number(line)) {
      /* we read something that isn't a number */
      /* we should tell the user he should do as we asked */
      continue; /* jump back to the beginning of the loop */
   }

   unsigned num = convert_number(line);
   unsigned area = calculate_area(num); /* do something with your input */
} while (true);
exit(FAILURE);

这里的代码并没有刻意让你看到你可以在某些地方做什么,但仍然忽略了实际的实现(为了你的练习)。请注意,检查一行是否实际上是数字的一种简单方法是转换。但是,并非所有要解析的内容都应检查有效性并同时进行处理。

另见(尤其是示例):

http://en.cppreference.com/w/cpp/string/basic_string/getline

http://en.cppreference.com/w/cpp/string/basic_string/stol

how to check if given c++ string or char* contains only digits?

【讨论】:

    【解决方案3】:
    do {
            cin.sync()
            cin.clear();
            cout << "Enter radius: ";
            cin >> input;
    } while(input < 0 && cin.fail());
    

    【讨论】:

    • 我不认为这会起作用,因为如果我输入一个number &lt; 0,它将跳出循环,因为cin 将存储该值,因为它是一个负数,并且不会失败.为了使您的条件起作用,&amp;&amp; 运算符的两边都必须是 true 才能继续循环,它必须是 ||
    猜你喜欢
    • 2014-06-30
    • 1970-01-01
    • 2016-05-09
    • 2013-01-06
    • 1970-01-01
    • 2011-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多