将字符串(以char*形式给出)解析为int的C++方式是什么?强大而清晰的错误处理是一个优点(而不是返回零).
这是我的第一条建议:不要使用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")
将很乐意回归true
并i
获得价值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)
它导致最小的代码大小
绝对没有充分的理由使用任何其他方法.
在新的C++ 11中有以下功能:stoi,stol,stoll,stoul等.
int myNr = std::stoi(myString);
它会在转换错误上引发异常.
即使这些新功能仍然存在与 Dan 所述相同的问题:他们很乐意将字符串"11x"转换为整数"11".
查看更多:http://en.cppreference.com/w/cpp/string/basic_string/stol
这是比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对原帖的评论)
你可以使用Boostlexical_cast
,它将它包装在一个更通用的界面中.
lexical_cast
抛出bad_lexical_cast
失败.
良好的'老C方式仍然有效.我推荐strtol或strtoul.在返回状态和'endPtr'之间,您可以提供良好的诊断输出.它还可以很好地处理多个基础.
您可以使用C++标准库中的字符串流:
stringstream ss(str); int x; ss >> x; if(ss) { // <-- error handling // use x } else { // not a number }
如果在尝试读取整数时遇到非数字,则流状态将设置为失败.
请参阅流陷阱,了解C++中错误处理和流的缺陷.
你可以使用stringstream
int str2int (const string &str) { stringstream ss(str); int num; ss >> num; return num; }
在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 }; templateinline 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
我认为这三个链接总结了:
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; }
如果你有C++ 11,适当的解决方案,现在是C++整数转换功能
:stoi
,stol
,stoul
,stoll
,stoull
.如果输入错误,它们会抛出适当的异常,并使用strto*
引擎盖下的快速和小功能.
如果您坚持使用早期版本的C++,那么在您的实现中模仿这些函数将是您的前瞻性.
从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; } }
作为奖励,它还可以处理其他基础,如十六进制.