【问题标题】:C++: check if string is a valid integer using "strtol"C++:使用“strtol”检查字符串是否为有效整数
【发布时间】:2016-10-23 17:07:05
【问题描述】:

我听说我应该使用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),它返回:

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

很公平。但是,当我用Hello world! 尝试它时,它错误地认为它是一个有效的int 并返回0

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

注意事项:

  • 我在 Windows 上使用 Code::Blocks 和 GNU GCC 编译器
  • 在“编译器标志”中选中“让 g++ 遵循 C++11 ISO C++ 语言标准 [-std=c++11]”。

【问题讨论】:

  • 将适当的参数而不是NULL 传递给strtol 以了解有效范围。
  • No repro 用于您的第二个测试用例。
  • @πάνταῥεῖ,这取决于long 是否可以容纳该数字。 32 位 long 不能。

标签: c++ windows c++11 error-handling strtol


【解决方案1】:

strtol 在第一个非数字处停止

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

如果 endptr 不为 NULL,strtol() 存储第一个地址 *endptr 中的无效字符。如果根本没有数字, strtol() 将 nptr 的原始值存储在 *endptr 中(并返回 0)。 特别是,如果 *nptr 不是 '\0' 但 **endptr 是 '\0' on 返回,整个字符串都有效

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

【讨论】:

【解决方案2】:

根据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 == '\0';
}

在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 并且代码被简化了。

【讨论】:

  • 我听说应该尽可能避免使用try
  • @FluorescentGreen5,当前关于 try/catch 的最佳实践是,应该优先考虑健壮的可读代码,因此有时 try/catch 将是选项。此外,一些新的 C++11 库功能需要 try/catch 才能获得所需的功能,如上面的 std::stol 示例所示。
  • 你的第一个代码 sn-p 完成了这项工作,但是,我建议你调用函数“isInt”,因为它不会将十进制数计算为数字
  • 另外,在你的第二次 sn-p 中,我得到 error: 'stol' was not declared in this scope,即使包括 iostreamstringstring.hcstringstdlib.hstdio.h
  • @FluorescentGreen5。您必须使用 C++11 g++ -std=c++11 your_file.cc -o your_program 编译并包括 string 标头。
【解决方案3】:

不要使用带有异常的 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())); 
}

【讨论】:

  • 它只是检查它是否只有数字。我需要能够确认签名/未签名的数字是否溢出。我应该坚持使用提供的其他方法吗?
  • 就用这个: 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(); }
  • 对不起,shift + enter 在这个快速评论中不起作用我无法删除它:)
  • 然后将其作为答案发布
猜你喜欢
  • 2021-10-03
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多