C++:使用 "strtol" 检查字符串是否为有效整数

C++: check if string is a valid integer using "strtol"

我听说我应该使用 strtol 而不是 atoi,因为它可以更好地处理错误。我想通过查看是否可以使用此代码检查字符串是否为整数来测试 strtol

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
    string testString = "ANYTHING";
    cout << "testString = " << testString << endl;
    int testInt = strtol(testString.c_str(),NULL,0);
    cout << "errno = " << errno << endl;
    if (errno > 0)
    {
        cout << "There was an error." << endl;
        cout << "testInt = " << testInt << endl;
    }
    else
    {
        cout << "Success." << endl;
        cout << "testInt = " << testInt << endl;
    }
    return 0;
}

我用 5 替换了 ANYTHING 并且效果很好:

testString = 5
errno = 0
Success.
testInt = 5

当我使用 2147483648 时,最大可能的 int + 1 (2147483648),它 returns 这个:

testString = 2147483648
errno = 34
There was an error.
testInt = 2147483647

很公平。但是,当我尝试使用 Hello world! 时,它错误地认为它是有效的 int 和 returns 0:

testString = Hello world!
errno = 0
Success.
testInt = 0

备注:

strtol 在第一个非数字处停止

但是如果你阅读手册页 http://man7.org/linux/man-pages/man3/strtol.3.html 你可以看到

If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0). In particular, if *nptr is not '[=11=]' but **endptr is '[=11=]' on return, the entire string is valid.

string testString = "ANYTHING";
cout << "testString = " << testString << endl;
char *endptr;
int testInt = strtol(testString.c_str(),&endptr,0);
if(**endptr)
   cout << "bad input";

根据the man page of strtol。您必须定义您的函数,例如:

bool isNumeric(const std::string& str) {
    char *end;
    long val = std::strtol(str.c_str(), &end, 10);
    if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
        //  if the converted value would fall out of the range of the result type.
        return false;   
    }
    if (end == str) {
       // No digits were found.
       return false;
    }
    // check if the string was fully processed.
    return *end == '[=10=]';
}

在C++11中,我比较喜欢用std::stol而不是std::strtol,比如:

bool isNumeric(const std::string& str) {
    try {
        size_t sz;
        std::stol(str, &sz);
        return sz == str.size();
    } catch (const std::invalid_argument&) {
        // if no conversion could be performed.
        return false;   
    } catch (const std::out_of_range&) {
        //  if the converted value would fall out of the range of the result type.
        return false;
    }
}

std::stol 调用 std::strtol,但您直接使用 std::string 并简化了代码。

不要使用带有异常的C++11方式解决方案,因为它比较慢。这是一个快速的 C++11 版本:

#include <algorithm>
bool is_decimal(const std::string& s)
{
    return !s.empty() && std::find_if(s.begin(), s.end(), [](char c){ return !std::isdigit(c); }) == s.end(); 
}

如果您确定您的字符串大部分不为空,那么您可以删除!s.empty()。如果不是,请保留它,因为 !s.empty() (!(s.length()==0) ) 比用空字符串调用 find_if (reference) 便宜。

编辑: 如果您必须处理溢出,请使用上面的异常版本。只有当你不能使用例外时才使用这个:

#include <string>
#include <sstream>
#include <limits>

template <class T>
bool is_decimal_and_fit(const std::string& s)
{
    long double decimal = 0;
    return (!(std::istringstream(s) >> decimal).fail() && (decimal >= std::numeric_limits<T>::lowest()) && (decimal <= std::numeric_limits<T>::max())); 
}