什么是最好的方法来做C ++的输入验证与cin? [英] What is the best way to do input validation in C++ with cin?

查看:161
本文介绍了什么是最好的方法来做C ++的输入验证与cin?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的哥哥最近开始学习C ++。他告诉我他在一个简单的程序中验证输入时遇到的问题。他有一个文本菜单,用户输入一个整数选择,如果他们输入一个无效的选择,他们将被要求再次输入(do while循环)。但是,如果用户输入一个字符串而不是一个int,代码将会中断。
我读了stackoverflow的各种问题,并告诉他重写他的代码沿着:

 #include< iostream> ; 
using namespace std;

int main()
{
int a;
do
{
cout<<\\\
输入数字:
cin>> a;
if(cin.fail())
{
//清除失败状态。
cin.clear();
//忽略其余的错误用户输入,直到行的结尾。
cin.ignore(std :: numeric_limits< std :: riversize> :: max(),\
'\\\
');
}
} while(true);
return 0;
}

虽然这个工作确定,我也尝试了一些其他的想法: >
1.使用try catch块。它没有工作。我认为这是因为一个异常不会引起由于错误的输入。
2.我尝试了 if(!cin){// Do Something} 这也没有工作。我还没有想到这一个。

3.第三,我尝试输入一个固定长度的字符串,然后解析它。我会使用atoi()。这个标准是否符合和便携?我应该编写自己的解析函数吗?

4.如果写一个使用cin的类,但动态地做这种错误检测,也许通过在运行时确定输入变量的类型,开销太大?是否有可能?



我想知道什么是做这种检查的最好方法,最好的做法是什么?



我想补充一点,虽然我不是编写C ++代码的新手,但我是写新的良好标准兼容代码的新手。我试图不学习坏习惯,学习正确的做法。



编辑:我看到litb已回答我之前的一个编辑。

 #include< iostream> 
using namespace std;

int main()
{
int a;
bool inputCompletionFlag = true;
do
{
cout<<\\\
输入数字:
cin>> a;
if(cin.fail())
{
//清除失败状态。
cin.clear();
//忽略其余的错误用户输入,直到行的结尾。
cin.ignore(std :: numeric_limits< std :: riversize> :: max(),\
'\\\
');
}
else
{
inputCompletionFlag = false;
}
} while(!inputCompletionFlag);
return 0;
}

此代码在输入时失败,例如1asdsdf。我不知道如何解决它,但litb已发布了一个很好的答案。 :)

解决方案

以下代码可用于确保您也拒绝

  42crap 

按照数字。如果你读取整行然后解析它并执行适当的操作,它可能需要你改变你的程序的工作方式。如果你的程序从不同的地方读取你的数字,直到现在,你必须放在一个中心的地方,解析一行输入,并决定的行动。但也许这也是一件好事 - 所以你可以通过分开的方式增加代码的可读性: I nput - P rocessing - O utput



无论如何,这里是如何拒绝上面的数字非数字。将一行写入一个字符串,然后用 stringstream



std :: string getline(){
std :: string str;
std :: getline(std :: cin,str);
return str;
}

int选择;
std :: istringstream iss(getline());
iss>>选择>> std :: ws;
if(iss.fail()||!iss.eof()){
// handle failure
}

它会删除所有结尾的空格。当它在读取整数或尾随空格时,遇到stringstream的文件结尾,然后设置eof位,我们检查它。如果它首先读取任何整数失败,则失败或坏位将被设置。



此答案的早期版本直接使用 std :: cin - 但 std :: ws 不能与连接到终端的 std :: cin 一起使用(它将阻塞而不是等待用户输入内容)所以我们使用 stringstream 读取整数。






回答您的一些问题:



问题: 1.使用try catch块。它没有工作。我认为这是因为输入错误不会引发异常。



回答: 告诉流在你读东西时抛出异常。你可以使用 istream :: exceptions 函数,告诉你想要抛出异常的错误类型:

  iss.exceptions(ios_base :: failbit); 

我从来没有使用过。如果你在 std :: cin 上这样做,你必须记住恢复其他依赖它的读者不会抛出的标志。发现只需使用函数 fail bad 即可更容易地询问流的状态。

问题: 2.我尝试了 if(!cin){// Do Something} 这也没有工作。

这可能是因为你给了它像42crap。对于流,这是完全有效的输入,当提取到一个整数。



问题: 3.第三,我尝试输入固定长度的字符串,然后解析它。我会使用atoi()。这个标准是否符合和便携?我应该自己编写解析函数吗?



答案: atoi是标准兼容。但是,当你想检查错误是不好的。没有错误检查,由它做,而不是其他功能。如果你有一个字符串,并希望检查它是否包含一个数字,那么就像在上面的初始代码。



有C类函数可以直接从C字符串读取。它们的存在允许与旧的,遗留代码的交互和编写快速执行的代码。在程序中应该避免使用它们,因为它们的工作相当低级,需要使用裸裸指针。由于它们的本质,它们不能被增强以与用户定义的类型一起工作。具体来说,这里谈到的函数strtol(字符串到长),它基本上是atoi与错误检查和能力与其他基地(例如hex)一起工作。



问题: 4.如果写一个使用cin的类,但动态地做这种错误检测,也许通过在运行时确定输入变量的类型,会有太多的开销吗?是否有可能?



答案: 一般来说, (如果你的意思是运行时开销)。但它取决于你在哪里使用该类。这个问题将是非常重要的,如果你正在编写一个高性能系统,处理输入和需要高的整个。但是如果你需要从终端或文件读取输入,你已经看到了这一点:等待用户输入东西需要很长时间,你不需要在这一点观察运行时成本了规模。



如果你的意思是代码开销 - 这取决于代码的实现。您需要扫描您读取的字符串 - 无论它是否包含数字,是否包含任意字符串。根据你想要扫描什么(也许你有一个日期输入或一个时间输入格式)。查看 boost.date_time 可以变得任意复杂。对于简单的事情,如数字之间分类或不,我想你可以逃脱与少量的代码。


My brother recently started learning C++. He told me a problem he encountered while trying to validate input in a simple program. He had a text menu where the user entered an integer choice, if they entered an invalid choice, they would be asked to enter it again (do while loop). However, if the user entered a string instead of an int, the code would break. I read various questions on stackoverflow and told him to rewrite his code along the lines of:

#include<iostream>
using namespace std;

int main()
{
    int a;
    do
    {
    cout<<"\nEnter a number:"
    cin>>a;
        if(cin.fail())
        {
            //Clear the fail state.
            cin.clear();
            //Ignore the rest of the wrong user input, till the end of the line.
            cin.ignore(std::numeric_limits<std::streamsize>::max(),\
                                  					'\n');
        }
    }while(true);
    return 0;
}

While this worked ok, I also tried a few other ideas:
1. Using a try catch block. It didn't work. I think this is because an exception is not raised due to bad input. 2. I tried if(! cin){//Do Something} which didn't work either. I haven't yet figured this one out.
3. Thirdly, I tried inputting a fixed length string and then parsing it. I would use atoi(). Is this standards compliant and portable? Should I write my own parsing function?
4. If write a class that uses cin, but dynamically does this kind of error detection, perhaps by determining the type of the input variable at runtime, would it have too much overhead? Is it even possible?

I would like to know what is the best way to do this kind of checking, what are the best practices?

I would like to add that while I am not new to writing C++ code, I am new to writing good standards compliant code. I am trying to unlearn bad practices and learn the right ones. I would be much obliged if answerers give a detailed explanation.

EDIT: I see that litb has answered one of my previous edits. I'll post that code here for reference.

#include<iostream>
using namespace std;

int main()
{
    int a;
    bool inputCompletionFlag = true;
    do
    {
    cout<<"\nEnter a number:"
    cin>>a;
        if(cin.fail())
        {
            //Clear the fail state.
            cin.clear();
            //Ignore the rest of the wrong user input, till the end of the line.
            cin.ignore(std::numeric_limits<std::streamsize>::max(),\
                                  					'\n');
        }
        else
        {
            inputCompletionFlag = false;
        }
    }while(!inputCompletionFlag);
    return 0;
}

This code fails on input like "1asdsdf". I didn't know how to fix it but litb has posted a great answer. :)

解决方案

Here is code you could use to make sure you also reject things like

42crap

Where non-number characters follow the number. If you read the whole line and then parse it and execute actions appropriately it will possibly require you to change the way your program works. If you program read your number from different places until now, you then have to put one central place that parses one line of input, and decides on the action. But maybe that's a good thing too - so you could increase the readability of the code that way by having things separated: Input - Processing - Output

Anyway, here is how you can reject the number-non-number of above. Read a line into a string, then parse it with a stringstream:

std::string getline() {
  std::string str;
  std::getline(std::cin, str);
  return str;
}

int choice;
std::istringstream iss(getline());
iss >> choice >> std::ws;
if(iss.fail() || !iss.eof()) {
  // handle failure
}

It eats all trailing whitespace. When it hit the end-of-file of the stringstream while reading the integer or trailing whitespace, then it sets the eof-bit, and we check that. If it failed to read any integer in the first place, then the fail or bad bit will have been set.

Earlier versions of this answer used std::cin directly - but std::ws won't work well together with std::cin connected to a terminal (it will block instead waiting for the user to input something), so we use a stringstream for reading the integer.


Answering some of your questions:

Question: 1. Using a try catch block. It didn't work. I think this is because an exception is not raised due to bad input.

Answer: Well, you can tell the stream to throw exceptions when you read something. You use the istream::exceptions function, which you tell for which kind of error you want to have an exception thrown:

iss.exceptions(ios_base::failbit);

I did never use it. If you do that on std::cin, you will have to remember to restore the flags for other readers that rely on it not throwing. Finding it way easier to just use the functions fail, bad to ask for the state of the stream.

Question: 2. I tried if(!cin){ //Do Something } which didn't work either. I haven't yet figured this one out.

Answer: That could come from the fact that you gave it something like "42crap". For the stream, that is completely valid input when doing an extraction into an integer.

Question: 3. Thirdly, I tried inputting a fixed length string and then parsing it. I would use atoi(). Is this standards compliant and portable? Should I write my own parsing function?

Answer: atoi is Standard Compliant. But it's not good when you want to check for errors. There is no error checking, done by it as opposed to other functions. If you have a string and want to check whether it contains a number, then do it like in the initial code above.

There are C-like functions that can read directly from a C-string. They exist to allow interaction with old, legacy code and writing fast performing code. One should avoid them in programs because they work rather low-level and require using raw naked pointers. By their very nature, they can't be enhanced to work with user defined types either. Specifically, this talks about the function "strtol" (string-to-long) which is basically atoi with error checking and capability to work with other bases (hex for example).

Question: 4. If write a class that uses cin, but dynamically does this kind of error detection, perhaps by determining the type of the input variable at runtime, would it have too much overhead? Is it even possible?

Answer: Generally, you don't need to care too much about overhead here (if you mean runtime-overhead). But it depends specifically on where you use that class. That question will be very important if you are writing a high performance system that processes input and needs to have high throughout. But if you need to read input from a terminal or a file, you already see what this comes down to: Waiting for the user to input something takes really so long, you don't need to watch runtime costs at this point anymore on this scale.

If you mean code overhead - well it depends on how the code is implemented. You would need to scan your string that you read - whether it contains a number or not, whether some arbitrary string. Depending on what you want to scan (maybe you have a "date" input, or a "time" input format too. Look into boost.date_time for that), your code can become arbitrary complex. For simple things like classifying between number or not, i think you can get away with small amount of code.

这篇关于什么是最好的方法来做C ++的输入验证与cin?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆