在C++中检查变量类型

6

我目前正在学习C++并决定制作一个测试我已经学到的技能的程序。现在在我的代码中,我想检查用户输入的值是否为double类型,如果不是double类型,我将放置一个if循环并要求他们重新输入。我遇到的问题是如何检查用户输入的变量类型,例如,如果用户输入char或string,我可以输出错误消息。以下是我的代码:

//cubes a user entered number
#include <iostream>
using namespace std;

double cube(double n); //function prototype

int main()
{
    cout << "Enter the number you want to cube: "; //ask user to input number
    double user;
    cin >> user;  //user entering the number

    cout << "The cube of " << user << " is " << cube(user) << "." << endl; //displaying the cubed number

    return 0;
}

double cube (double n) //function that cubes the number
{
    return n*n*n; // cubing the number and returning it
}

编辑:我必须说我刚开始,对你的代码一无所知,但我会查看你的链接。顺便说一下,我还没有学习如何使用模板,我正在学习处理数据,只有C++ Primer Plus第5版的第3章。


这在C++中并非真正可行,除非进行大量繁琐的工作或使用库。我建议今天等待一下,尝试做些不同的事情。一个月或两个月后再回来看看。 - Paul Nathan
5个回答

12

安全的 C++ 方法

您可以使用 std::istringstream 定义一个函数来实现此功能:

#include <sstream>  

bool is_double(std::string const& str) {
    std::istringstream ss(str);

    // always keep the scope of variables as close as possible. we see
    // 'd' only within the following block.
    {
        double d;
        ss >> d;
    }

    /* eat up trailing whitespace if there was a double read, and ensure
     * there is no character left. the eof bit is set in the case that
     * `std::ws` tried to read beyond the stream. */
    return (ss && (ss >> std::ws).eof());
}
为了帮助您理解它的作用(一些点已经简化):
  • 创建一个使用给定字符串初始化的输入字符串流
  • 使用operator>>从中读取双精度浮点数值。这意味着跳过空格并尝试读取双精度浮点数。
  • 如果无法读取双精度浮点数,例如abc,则流会设置fail标志位。请注意,类似3abc的情况将成功,并且不会设置失败位。
  • 如果设置了失败位,则ss求值为零值,表示false
  • 如果读取了双精度浮点数,我们跳过尾随空格。如果我们此时在流的末尾(请注意,如果我们尝试读取超出流的结尾,eof()将返回truestd::ws正是这样做的),则eof将返回true。 请注意,此检查确保3abc不会通过我们的检查。
  • 如果在&&两侧的情况都求值为true,我们向调用者返回true,表示给定的字符串是双精度浮点数。

类似地,您可以检查int和其他类型。如果您知道如何使用模板,则知道如何将其推广到其他类型。顺便说一下,这正是boost::lexical_cast为您提供的。请查看:http://www.boost.org/doc/libs/1_37_0/libs/conversion/lexical_cast.htm

C语言方法一

此方法有优点(速度快),但也有主要缺点(无法使用模板来泛化,需要使用原始指针):

#include <cstdlib>
#include <cctype>  

bool is_double(std::string const& s) {
    char * endptr;
    std::strtod(s.c_str(), &endptr);
    if(endptr != s.c_str()) // skip trailing whitespace
        while(std::isspace(*endptr)) endptr++;
    return (endptr != s.c_str() && *endptr == '\0');
}

strtod会将endptr设置为最后一个处理的字符,我们的情况下是终止空字符。如果没有进行转换,则将 endptr 设置为传递给 strtod 的字符串的值。

在 C 语言中,另一种方法是使用std::sscanf函数。但是很容易忽略某些细节。以下是正确的使用方式:

#include <cstdio>

bool is_double(std::string const& s) {
    int n;
    double d;
    return (std::sscanf(s.c_str(), "%lf %n", &d, &n) >= 1 && 
            n == static_cast<int>(s.size()));
}

std::sscanf函数会返回已转换的项。虽然标准规定%n不包含在计数中,但是多个来源互相矛盾。最好使用>=进行比较以确保正确(请参见sscanf的手册)。n将被设置为处理的字符数量。它将与字符串的大小进行比较。两个格式说明符之间的空格表示可选的尾随空格。

结论

如果您是初学者,请了解std::stringstream并按照C++的方式操作。在掌握C++的基本概念之前最好不要涉及指针。


我不建议不必要的作用域限制;在我看来,这只会使代码更难读。 - strager
在我看来,告诉他们这件事是一个好习惯 :) 但他成为大牌后是否会这样做就另说了。 - Johannes Schaub - litb
在使用 %lf 和 %n 之间需要加空格吗?这会影响结果吗? - strager
strager。是的。一个空格表示任何或零个空格。没有空格,“3.14 ”将分配n=4。 - Johannes Schaub - litb

7

在标准库中没有适合的方法来检查一个字符串是否真正包含一个双精度浮点数。你可能需要使用Boost。以下解决方案受到C++ Cookbook食谱3.3的启发:

#include <iostream>
#include <boost/lexical_cast.hpp>
using namespace std;
using namespace boost;

double cube(double n);

int main()
{
    while(true)
    {
        cout << "Enter the number you want to cube: ";
        string user;
        cin >> user;

        try
        {
            // The following instruction tries to parse a double from the 'user' string.
            // If the parsing fails, it raises an exception of type bad_lexical_cast.
            // If an exception is raised within a try{ } block, the execution proceeds
            // with one of the following catch() blocks
            double d = lexical_cast <double> (user);   

            cout << "The cube of " << d << " is " << cube(d) << "." << endl;
            break;
        }
        catch(bad_lexical_cast &e)
        {
            // This code is executed if the lexical_cast raised an exception; We
            // put an error message and continue with the loop
            cout << "The inserted string was not a valid double!" << endl;
        }
    }
    return 0;
}

double cube (double n)
{
    return n*n*n;
}

现在不用担心模板。把lexical_cast<double>()看作一个尝试将值转换为double并返回double的函数即可。 - Federico A. Ramponi

1

sscanf可以实现你想要的功能;它会返回正确处理的参数数量。以下是一个简单的示例:

//cubes a user entered number
#include <iostream>
#include <cstdio>
using namespace std;

double cube(double n); //function prototype

int main()
{
        cout << "Enter the number you want to cube: "; //ask user to input number
        string user;
        cin >> user;  //user entering the number

        // Convert the number to a double.
        double value;
        if(sscanf(user.c_str(), "%lf", &value) != 1)
        {
                cout << "Bad!  " << user << " isn't a number!" << endl;
                return 1;
        }

        cout << "The cube of " << user << " is " << cube(user) << "." << endl; //displaying the cubed number

        return 0;
}

double cube (double n) //function that cubes the number
{
        return n*n*n; // cubing the number and returning it
}

在其他答案中发布的其他方法都有其优点和缺点。这种方法存在尾随字符问题,而且不是“C ++”风格。


哦,是啊,为什么我没想到用sscanf。如果你不需要通用功能,这可能更好。然而,在C++中,你需要包含cstdio头文件并使用std::sscanf :) - Johannes Schaub - litb
然而,为了确定字符串是否真正包含 double 类型(而不是像 "3a" 这样的东西),需要做更多的工作。对此,%n 是一个不错的工具。我认为,最简单的方法是使用 strtod(并且 *lastConvert == '\0')。而最安全的方法则是使用 stringstream。 - Johannes Schaub - litb

0

我必须说我刚刚开始,对于你的代码一无所知,但我会查看你的链接。顺便说一下,我还没有学习如何使用模板,我正在学习处理数据,只有在我的C++ Primer Plus第五版的第三章。


我把你的答案放到了问题里。你以后应该这样做答案。 - Johannes Schaub - litb

0

你可以退而求其次,使用C语言并使用strtod函数。

你的程序读入一个字符串,然后将其传递给一个尝试将该字符串转换为双精度浮点型数值的函数。

bool is_double(const char* strIn, double& dblOut) {
    char* lastConvert = NULL;
    double d = strtod(strIn, &lastConvert);
    if(lastConvert == strIn){
        return false;
    } else {
       dblOut = d;
       return true;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接