【问题标题】:Validating double values input验证双精度值输入
【发布时间】:2021-07-01 03:03:17
【问题描述】:

我编写了一个程序,通过用户输入计算圆柱和矩形区域。但我想改进我的代码。当用户输入字符而不是数字时,它应该输出错误消息。所以我写了一些额外的代码,但它不能正常工作。

#include <iostream>
#include <ctype.h>

using namespace std;
class area_cl
{
public:
    double height, width;
};
class rectangle : public area_cl
{
public:
    double area(double h, double w)
    {
        return h * w;
    }
};
class cylinder : public area_cl
{
private:
    double pi = 3.14;

public:
    double area(double h, double w)
    {
        return 2 * pi * (w / 2) * ((w / 2) + h);
    }
};
int main()
{
    area_cl dimension;
    rectangle obj1;
    cylinder obj2;
    bool flag = true;

    while (flag)
    {

        cout << "What is the dimensions? (Height and Width)" << endl;

        cin >> dimension.height >> dimension.width;

        if (isdigit(dimension.height) && isdigit(dimension.width))
        {
            flag = false;
        }
        else
        {
            cout << "You are not entered number,please try again." << endl;
        }
    }

    cout << "Rectangle's area is : " << obj1.area(dimension.height, dimension.width) << endl;
    cout << "Cylinder's area is : " << obj2.area(dimension.height, dimension.width) << endl;
    return 0;
}
  1. 我曾想过使用isdigit(),但我的输入变量必须是双精度类型,而且我的代码可能会因此而崩溃。 C++中有没有什么方法,比如C#中的解析?

  2. 我还考虑过使用 ASCII 码控制输入。例如if ( char variable &gt;= 48 &amp;&amp; char variable &lt;= 57),但我无法让它工作。

我更愿意用第一个选项来解决这个问题,但我完全愿意接受其他解决方案。

谢谢。

【问题讨论】:

标签: c++ validation input io cin


【解决方案1】:

1.我想过使用isdigit(),但我的输入变量必须是双精度类型,我的代码可能会因此而崩溃。 C++中有没有什么方法,比如C#中的解析?

isdigit 只是检查数字,它不适合你需要的东西。是的,在 C++ 中有更好的解析方式,虽然不像 C#,但至少和 IMO 一样好(我会提前回过头来)。


2.我还考虑过使用 ASCII 码控制输入。例如if ( char variable &gt;= 48 &amp;&amp; char variable &lt;= 57),但我无法让它工作。

这也不是一个好选择,您需要doubles,这会使逐位检查变得不必要地复杂,尤其是因为这可以通过其他方式轻松实现。

为了以后参考,在使用字符比较时,应该使用字符文字,并非所有编码的数字代码都与ASCII相同:

if (char variable >= '0' && char variable <= '9')

会更合适。


我更愿意用第一个选项来解决这个问题,但我完全愿意接受其他解决方案。

我能想到的更简单、更惯用的方法是使用cin返回值来检查输入是否确实有效,如果不是则采取措施(清除缓冲区和重置流错误状态),然后再次询问对于有效值,重构的代码类似于:

while (flag)
{
    cout << "What are the dimensions? (Height and Width)" << endl;
    if (cin >> dimension.height && cin >> dimension.width)
    {
        flag = false;
    }
    else
    {
        cin.clear(); // clear error flags
        cin.ignore(numeric_limits<streamsize>::max(),'\n'); // clear buffer *
        cout << "You have not entered numbers, please try again" << end;
    }
}

或者,更好的 IMO:

cout << "What are the dimensions? (Height and Width)" << endl << ">"; // **
while (!(cin >> dimension.height && cin >> dimension.width))           
{
    cin.clear(); // clear error flags
    cin.ignore(numeric_limits<streamsize>::max(), '\n'); // clear buffer *
    cout << "You have not entered numbers, please try again" << endl << ">";
}

* - cin.ignore(numeric_limits&lt;streamsize&gt;::max(),'\n'); 可能需要包含 &lt;limits&gt; 标头。

此例程清除输入缓冲区,其中将包含无法解析的字符,以防发生读取错误。

你可以这样读:

“在找到换行符之前,忽略(并从缓冲区中删除)所有存在的字符”

这将删除所有字符,直到找到 \n,这是输入缓冲区中的最后一个字符,当您按 Enter 时添加。


** - 请注意,我在第二个选项中更改了消息。输入消息仅在循环开始时发出一次,错误消息仅在输入失败时询问新的输入。您现在拥有它的方式,如果输入失败,消息流将是:

What is the dimensions? (Height and Width)
1.9 a
What is the dimensions? (Height and Width)
You are not entered number,please try again 
_

我更喜欢:

What are the dimensions? (Height and Width)
> 1.9 a
You have not entered numbers, please try again 
>_

【讨论】:

  • 我喜欢压缩版本,但是,我认为 while 条件下的 cout 会改变行为。它也很难阅读。我认为 do-while 循环更符合意图(至少执行一次)。
  • @AllanWind 我想试试……虽然会让事情更清楚一点,是的。
  • 这很奇怪,因为您现在在触发错误的调用之前处理错误。用非 goto 版本更新了我的答案,因为这似乎是传达我的想法的最简单方法。我使用 for(;;) 因为它的类型更短,但我仍然认为 do-while 更好地表达了意图。我希望我没有把你误入歧途。
  • @AllanWind 的想法是保持消息井然有序(正如 OP 所拥有的那样),首先是错误消息,然后是请求输入消息,我认为可以只要求输入一个循环之前的时间,但要求输入并下一次发出错误消息不是一个好的流程。
  • 我认为只需注意“What are the ...”仅显示一次的更改行为即可。
【解决方案2】:

isdigit() 检查字符是否为数字。当你传给它一个双精度值时,它会被转换成一个整数。假设您的输入是 1.2,它被转换为 (int) 1,它不是数字 '0' 到 '9'。

您可以使用cin.fail() 来确定它是否能够获取输入。如果不是,清除失败状态,并用cin.ignore() 刷新标准输入的无法读取的数据。 cin.bad()cin.eof() 以类似的方式覆盖:

#include <limits>
#include <stdlib.h>

...

retry:
    cout << "What is the dimensions? (Height and Width)" << endl;
    cin >> dimension.height >> dimension.width;
    if(cin.bad()) return EXIT_FAILURE;
    if(cin.eof()) return EXIT_SUCCESS;
    if(cin.fail()) {
        cout << "You have not entered number, please try again" << endl << ">";
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        goto retry;
    }

或非 goto 变体(与@anastaciu 讨论):

#include <limits>
#include <stdlib.h>

...

for(;;) {
    cout << "What is the dimensions? (Height and Width)" << endl;
    cin >> dimension.height >> dimension.width;
    if(cin.bad()) return EXIT_FAILURE;
    else if(cin.eof()) return EXIT_SUCCESS;
    else if(cin.fail()) {
        cout << "You have not entered number, please try again" << endl << ">";
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    } else
        break;
}

另一种选择是读取一行文本并对其进行解析。与丢失所有用户输入相比,它可以让您提供更好的错误处理。

【讨论】:

  • 不是我的反对意见。但是很多用户不是很喜欢 goto,这很可能是原因。您可以找到许多谴责其用法的帖子,例如 stackoverflow.com/q/3517726/6865932
  • 看起来比替代品bool flag=true; while(flag) { if() flag=false; else .. } 更干净。理性的人可以不同意。如果没有帮助,我会删除答案。
  • 我个人对此没有任何问题,我只是提醒您为什么选择 DV,因为没有来自投票者的反馈,而且答案似乎没有错。从我的角度来看,不需要删除。
  • 别担心,伙计。
  • goto 绝对没有问题。也许大部分使用 C++ 的人不知道什么是无条件的jmp 指令,但这并不等于goto 有什么问题。我唯一的意见是检查cin.bad()cin.eof() 以涵盖罕见但不可恢复的流问题,并允许用户通过输入手动EOF 来取消输入,例如ctrl + d(Windows 上的ctrl + z
猜你喜欢
  • 1970-01-01
  • 2019-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-01
  • 1970-01-01
  • 2017-04-30
相关资源
最近更新 更多