当前位置:  开发笔记 > 编程语言 > 正文

如何在C++中将字符串解析为int?

如何解决《如何在C++中将字符串解析为int?》经验,为你挑选了11个好方法。

将字符串(以char*形式给出)解析为int的C++方式是什么?强大而清晰的错误处理是一个优点(而不是返回零).



1> Dan Moulding..:

什么不该做

这是我的第一条建议:不要使用stringstream.虽然起初它看起来很简单,但如果你想要健壮性和良好的错误处理,你会发现你必须做很多额外的工作.

这是一种直观地看起来应该工作的方法:

bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

这有一个主要问题:str2int(i, "1337h4x0r")将很乐意回归truei获得价值1337.我们可以通过确保stringstream转换后没有更多字符来解决此问题:

bool str2int (int &i, char const *s)
{
    char              c;
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail() || ss.get(c)) {
        // not an integer
        return false;
    }
    return true;
}

我们解决了一个问题,但还有其他一些问题.

如果字符串中的数字不是10?我们可以尝试通过ss << std::hex在尝试转换之前将流设置为正确的模式(例如)来容纳其他基础.但是,这意味着调用者必须知道的先验什么基地的数量-以及如何调用者可能知道?呼叫者不知道该号码是什么.他们甚至都不知道这一个数字!他们怎么能知道它是什么基础?我们可以强制要求输入到我们程序的所有数字都必须是10,并拒绝十六进制或八进制输入为无效.但这不是非常灵活或强大.这个问题没有简单的解决方案.你不能简单地为每个基数尝试一次转换,因为对于八进制数字(前导零),十进制转换将始终成功,并且对于某些十进​​制数字,八进制转换可能会成功.所以现在你必须检查一个前导零.可是等等!十六进制数也可以从前导零开始(0x ...).叹.

即使你在处理上述问题取得成功,还有另一个更大的问题:如果呼叫者需要糟糕输入区分(例如,"123foo")和一个数字,超出的范围int(例如,"40亿"为32位int)?有了stringstream,没有办法做出这种区分.我们只知道转换是成功还是失败.如果失败了,我们无法知道失败的原因.正如您所看到的,stringstream如果您想要稳健性和清晰的错误处理,还有很多不足之处.

这引出了我的第二条建议:不要lexical_cast为此使用Boost.考虑一下lexical_cast文档的内容:

如果需要对转换进行更高程度的控制,std :: stringstream和std :: wstringstream会提供更合适的路径.在需要非基于流的转换的情况下,lexical_cast是工作的错误工具,并非针对此类方案的特殊情况.

什么??我们已经看到stringstream控制水平很低,但是如果你需要"更高水平的控制" ,它stringstream应该被用来代替lexical_cast.此外,因为lexical_cast它只是一个包装器stringstream,它遇到了同样的问题stringstream:对多个数字基础的支持不足和错误处理不佳.

最好的解决方案

幸运的是,有人已经解决了上述所有问题.C标准库包含strtol和家庭没有这些问题.

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
        return OVERFLOW;
    }
    if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
        return UNDERFLOW;
    }
    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }
    i = l;
    return SUCCESS;
}

对于处理所有错误情况的东西非常简单,并且还支持从2到36的任何数字基数.如果base为零(默认值),它将尝试从任何基数转换.或者调用者可以提供第三个参数,并指定只应针对特定的基础进行转换.它功能强大,只需极少的工作量即可处理所有错误.

更喜欢strtol(和家庭)的其他理由:

它表现出更好的运行时性能

它引入了较少的编译时开销(其他人从头中获取了近20倍的SLOC)

它导致最小的代码大小

绝对没有充分的理由使用任何其他方法.


@JamesDunne:POSIX要求`strtol`是线程安全的.POSIX还要求`errno`使用线程本地存储.即使在非POSIX系统上,几乎所有多线程系统上的"errno"实现都使用线程本地存储.最新的C++标准要求`errno`符合POSIX标准.最新的C标准还要求`errno`具有线程本地存储.即使在Windows上,它肯定不符合POSIX标准,`errno`是线程安全的,并且,通过扩展,`strtol`也是如此.
@fuzzyTew我在`std :: stol`被添加到C++语言之前写了这个答案.也就是说,我认为这是"C++中的C编码"是不公平的.很明显,当std :: strtol`明确是C++语言的一部分时,它是C编码.我的答案在编写时完全适用于C++,即使使用新的`std :: stol`它仍然适用.调用可能抛出异常的函数并不总是最适合每种编程情况.
这在C++中是不合适的C编码.标准库包含`std :: stol`,它将适当地抛出异常而不是返回常量.
@fuzzyTew:磁盘空间不足是一种特殊情况.计算机生成的格式错误的数据文件是一种特殊情况.但用户输入中的拼写错误并不例外.有一种能够处理正常的,非常规的解析失败的解析方法是很好的.
我不能真正按照你的推理来反对使用boost :: lexical_cast.正如他们所说,std :: stringstream确实提供了很多控制 - 你可以做任何事情,从错误检查到确定自己的基础.当前的文档如下所示:"对于更复杂的转换,例如精度或格式需要比lexical_cast的默认行为更严格的控制,建议采用传统的std :: stringstream方法."
注意,标识符"OVERFLOW"和"UNDERFLOW"由`gcc`(因此是`g ++`)用于宏以与System V兼容.要禁用标准的扩展,例如这个,将`-ansi`传递给` g ++`命令行或makefile.http://sourceware.org/bugzilla/show_bug.cgi?id=5407 http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Dialect-Options.html
@DanMoulding,是的,我没有意识到写作时stol只是c ++ 11.但是,异常是在C++中处理异常条件的标准方法.返回错误常量是C范例,在C++中通常不赞成和意外,增加了调试和共享代码的障碍,并传播了视觉上膨胀的条件检查.它在C中是必需的,因为C没有例外.返回值通常会编译为更快的代码,因此如果代码被分析为在许多异常条件下存在瓶颈,则可能是最好的.

2> CC...:

在新的C++ 11中有以下功能:stoi,stol,stoll,stoul等.

int myNr = std::stoi(myString);

它会在转换错误上引发异常.

即使这些新功能仍然存在 Dan 所述相同的问题:他们很乐意将字符串"11x"转换为整数"11".

查看更多:http://en.cppreference.com/w/cpp/string/basic_string/stol


但他们接受的论点不止于此,其中一个是size_t的指针,如果不是null,则设置为第一个未转换的字符

3> Luka Marinko..:

这是比atoi()更安全的C方式

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

带有标准库stringstream的 C++ :(感谢CMS)

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

使用boost库:(谢谢jk)

#include 
#include 

try
{
    std::string str = "123";
    int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
    // Error
}

编辑:修复了字符串流版本,以便处理错误.(感谢CMS和jk对原帖的评论)


如果你想要像lexical_cast那样处理它,可以从((ss >> num).fail())将它改为(!(ss >> num).fail()&&(ss >> ws).eof())
带有标准库stringstream方法的C++对于诸如"12-SomeString"之类的字符串不起作用,即使使用.fail()检查也是如此.
你的stringstream版本会接受像"10haha"这样的东西而不会抱怨

4> jk...:

你可以使用Boostlexical_cast,它将它包装在一个更通用的界面中. lexical_cast(Source)抛出bad_lexical_cast失败.


提升lexical_cast是非常缓慢和痛苦的低效率.
@Matthieu对Boost的更新提高了性能:http://www.boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/performance.html(另请参阅http://stackoverflow.com/questions/1250795 /非常差的 - 运动 - 演员表演)

5> Chris Arguin..:

良好的'老C方式仍然有效.我推荐strtol或strtoul.在返回状态和'endPtr'之间,您可以提供良好的诊断输出.它还可以很好地处理多个基础.


当人们担心"更现代"的方法来解决问题时,这很有趣.
我看了其他的答案,到目前为止没有什么明显更好/更容易/更清洁或更安全.海报说他有一个char*.这限制了你将获得的安全数量:)
哦,在编写C++时请不要使用这个旧的C语言.在C++中有更好/更容易/更清洁/更现代/更安全的方法!

6> jk...:

您可以使用C++标准库中的字符串流:

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

如果在尝试读取整数时遇到非数字,则流状态将设置为失败.

请参阅流陷阱,了解C++中错误处理和流的缺陷.


即使使用"流状态"检查,C++ stringstream方法也不适用于"12-SomeString"等字符串.

7> CMS..:

你可以使用stringstream

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}


但这并不能解决任何错误.您必须检查流是否有故障.
即使使用"流状态"检查,C++ stringstream方法也不适用于"12-SomeString"等字符串

8> 小智..:

在C++工具包字符串库(StrTk)具有以下溶液:

static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
   0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 0xF8 - 0xFF
 };

template
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
   if (0 == std::distance(begin,end))
      return false;
   v = 0;
   InputIterator it = begin;
   bool negative = false;
   if ('+' == *it)
      ++it;
   else if ('-' == *it)
   {
      ++it;
      negative = true;
   }
   if (end == it)
      return false;
   while(end != it)
   {
      const T digit = static_cast(digit_table[static_cast(*it++)]);
      if (0xFF == digit)
         return false;
      v = (10 * v) + digit;
   }
   if (negative)
      v *= -1;
   return true;
}

InputIterator可以是unsigned char*,char*或std :: string迭代器,T应该是signed int,例如signed int,int或long


代码不处理溢出.`v =(10*v)+ digit;`使用文本值为'INT_MIN`的字符串输入不必要地溢出.表是值得怀疑的,只需`digit> ='0'&& digit <='9'

9> 小智..:

我认为这三个链接总结了:

http://tinodidriksen.com/2010/02/07/cpp-convert-int-to-string-speed/

http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/

http://www.fastformat.org/performance.html

stringstream和lexical_cast解决方案与使用stringstream的lexical cast大致相同.

词汇演员的一些专业使用不同的方法,请参阅http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp了解详情.整数和浮点数现在专门用于整数到字符串的转换.

人们可以根据自己的需要专门研究lexical_cast并快速完成.这将是满足各方的终极解决方案,简洁明了.

已经提到的文章显示了转换整数< - >字符串的不同方法之间的比较.以下方法有意义:旧的c-way,spirit.karma,fastformat,简单的天真循环.

在某些情况下,Lexical_cast是可以的,例如int到string的转换.

使用词法转换将字符串转换为int不是一个好主意,因为它比atoi慢10-40倍,具体取决于所使用的平台/编译器.

Boost.Spirit.Karma似乎是将整数转换为字符串的最快库.

ex.: generate(ptr_char, int_, integer_number);

从上面提到的文章中基本的简单循环是将字符串转换为int的最快方法,显然不是最安全的方法,strtol()似乎是一个更安全的解决方案

int naive_char_2_int(const char *p) {
    int x = 0;
    bool neg = false;
    if (*p == '-') {
        neg = true;
        ++p;
    }
    while (*p >= '0' && *p <= '9') {
        x = (x*10) + (*p - '0');
        ++p;
    }
    if (neg) {
        x = -x;
    }
    return x;
}



10> fuzzyTew..:

如果你有C++ 11,适当的解决方案,现在是C++整数转换功能:stoi,stol,stoul,stoll,stoull.如果输入错误,它们会抛出适当的异常,并使用strto*引擎盖下的快速和小功能.

如果您坚持使用早期版本的C++,那么在您的实现中模仿这些函数将是您的前瞻性.



11> Pharap..:

从C++ 17开始,您可以使用此处记录std::from_chars标题.

例如:

#include 
#include 
#include 

int main()
{
    char const * str = "42";
    int value = 0;

    std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);

    if(result.error == std::errc::invalid_argument)
    {
      std::cout << "Error, invalid format";
    }
    else if(result.error == std::errc::result_out_of_range)
    {
      std::cout << "Error, value too big for int range";
    }
    else
    {
      std::cout << "Success: " << result;
    }
}

作为奖励,它还可以处理其他基础,如十六进制.

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有