如何从字符串中获取一个无符号长整型?

8

如何在C++中安全且最佳地从字符串中检索无符号长整型?

我知道有很多可能的方法。

首先是将从atol获取的有符号长整型转换为无符号长整型。

char *myStr; // Initalized to some value somehow.
unsigned long n = ((unsigned)atol(myStr));

显而易见的问题是,当myStr中存储的值大于有符号长整型所能容纳的值时会发生什么?atol检索到什么?

下一个可能性是使用strtoul。

char *myStr; // Initalized to some value somehow.
unsigned long n = strtoul(myStr, 0, 10);

然而,这对我的需求来说有点过于复杂了。我需要一个简单的函数,输入字符串,输出十进制无符号长整型。此外,错误处理也有待改善。

我找到的最后一个可能性是使用sscanf。

char *myStr; // Initalized to some value somehow.
unsigned long n = 0;
if(sscanf(myStr, "%lu", n) != 1) {
    //do some error handling
}

再次强调,错误处理还有很大的提升空间,而且比我想象中要复杂一些。

剩下的明显选项是编写自己的代码,可以是对之前某个选项的包装器,也可以是手动循环遍历字符串并将每个数字手动转换,直到达到ULONG_MAX。

我的问题是,除了我谷歌搜索失败的选项外,还有其他的选项吗?C++ std 库中是否有干净地将字符串转换为无符号长整型并在出现错误时抛出异常的函数?

如果这是重复问题,我深感抱歉,但我找不到与我的问题完全匹配的问题。


atol: "如果正确的值超出了可表示的范围,则返回LONG_MAX或LONG_MIN。" http://www.cplusplus.com/reference/clibrary/cstdlib/atol/ - Mark Rushakoff
一些问题,我不确定是否重复:(1) https://dev59.com/J3M_5IYBdhLWcg3ww2AQ#1243435 (2) https://dev59.com/IknSa4cB1Zd3GeqPNWOv#1435268 - GManNickG
3
我不明白你反对strtoul的原因。它是这种情况下的完美函数。将基数设为0,它就会将带有任何基数前缀的字符串转换成数字,例如你的C库可以理解的"0x"。 - Zan Lynx
7个回答

6

您可以毫无问题地使用strtoul。该函数返回一个无符号长整型。如果转换无法执行,则函数返回0。如果正确的长整型值超出范围,则函数返回ULONG_MAX,并将errno全局变量设置为ERANGE。


如果可能的话,寻找异常风格的错误处理方式,而不是errno风格。 - Daniel Bingham
异常风格的错误处理,但没有通过类造成额外开销?在我看来有点矛盾 :) - Georg Fritzsche
函数可以进行异常风格的错误处理。我只是不想每次想要转换长整型时都实例化两个不需要的类。这似乎非常浪费! - Daniel Bingham

5

一种方法是:

stringstream(str) >> ulongVariable;

我编辑了问题,以更清晰地表明我在这种情况下使用的是C风格的字符串。因此,这将需要streamstream(string(myStr))>> ulongvariable。这对我来说感觉很浪费和低效:/我宁愿它是更简洁和直接的东西。 - Daniel Bingham
1
实际上,这并不是必要的,因为std::string有一个隐式构造函数,它以C风格的字符串作为参数。该构造函数允许您只编写stringstream("whatever") - hrnt
“Smack self”. 当然,这将导致实例化两个类 - 并且所有与内存管理相关的操作 - 而一个简单的带有单一内部循环的函数本应该能够完成。如果标准库中有类似的东西就好了。 - Daniel Bingham
好的C++编译器是积极的优化器。永远记住这一点。我不是说它是最快的方式,但它应该是相当快的。 - Mehrdad Afshari
1
@Alcon,在必要之前不要考虑性能问题。你确定这段代码需要非常快吗?你确定这个解决方案不是非常快的吗? - tster
如何从char *获取unsigned long long? - RedFox

4
template <class T>
T strToNum(const std::string &inputString,
           std::ios_base &(*f)(std::ios_base&) = std::dec)
{
    T t;
    std::istringstream stringStream(inputString);

    if ((stringStream >> f >> t).fail())
    {
        throw runtime_error("Invalid conversion");
    }
    return t;
}


// Example usage
unsigned long ulongValue = strToNum<unsigned long>(strValue);
int intValue             = strToNum<int>(strValue);

int intValueFromHex      = strToNum<int>(strHexValue,std::hex);
unsigned long ulOctValue = strToNum<unsigned long>(strOctVal, std::oct);

2
如果您可以使用Boost库(www.boost.org),请查看转换库 - 它是仅有头文件的包含。
#include "boost/lexical_cast.hpp"

那么你需要做的就是:
unsigned long ul = boost::lexical_cast<unsigned long>(str);

我会研究一下boost库,我正在考虑使用Python解释库,因此使用boost可能非常适合我的需求。不过,如果有一个简单干净的函数可供使用,那对于那些不想依赖boost库的人来说就更好了。 - Daniel Bingham

1

Jeffrey Stedfast写了一篇精美的文章,介绍了如何为Mono编写整数解析器程序(使用C语言)。
该程序生成使用本地类型的代码(需要32位来解析32位),并使用溢出错误码。


1
使用内置的“atol” std函数。
例如:std :: string input =“1024”;

std::atol(input.c_str());

Atol希望参数是c字符串类型,所以c_str()函数就是为了做这个而存在的。


0

一个健壮的方法是编写一个静态函数并使用它

bool str2Ulong(const string& str,unsigned long & arValue)
{
   char *tempptr=NULL;
   arValue=strtoul(str,tempptr,10);

   return ! (arValue==0 && tempptr==str.c_str());

}

1
这个例子是错误的,tempptr 是 (char*),而 strtoul 函数期望的是 (char**)。 - Noam Rathaus

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