如何在 C++ 中将字符串解析为 int?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/194465/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to parse a string to an int in C++?
提问by Eugene Yokota
What's the C++ way of parsing a string (given as char *) into an int? Robust and clear error handling is a plus (instead of returning zero).
将字符串(以 char * 形式给出)解析为 int 的 C++ 方法是什么?强大而清晰的错误处理是一个加号(而不是返回零)。
采纳答案by CC.
In the new C++11 there are functions for that: stoi, stol, stoll, stoul and so on.
在新的 C++11 中有这样的函数:stoi、stol、stoll、stoul 等等。
int myNr = std::stoi(myString);
It will throw an exception on conversion error.
它将在转换错误时抛出异常。
Even these new functions still have the same issueas noted by Dan: they will happily convert the string "11x" to integer "11".
即使这些新函数仍然存在与Dan 指出的相同的问题:它们很乐意将字符串“11x”转换为整数“11”。
See more: http://en.cppreference.com/w/cpp/string/basic_string/stol
查看更多:http: //en.cppreference.com/w/cpp/string/basic_string/stol
回答by Dan Moulding
What not to do
什么不该做
Here is my first piece of advice: do not use stringstream for this. While at first it may seem simple to use, you'll find that you have to do a lot of extra work if you want robustness and good error handling.
这是我的第一条建议:不要为此使用 stringstream。虽然乍一看使用起来似乎很简单,但您会发现如果您想要健壮性和良好的错误处理,您必须做很多额外的工作。
Here is an approach that intuitively seems like it should work:
这是一种直觉上似乎应该有效的方法:
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
This has a major problem: str2int(i, "1337h4x0r")
will happily return true
and i
will get the value 1337
. We can work around this problem by ensuring there are no more characters in the stringstream
after the conversion:
这有一个主要问题:str2int(i, "1337h4x0r")
会愉快地返回true
并i
获得 value 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;
}
We fixed one problem, but there are still a couple of other problems.
我们解决了一个问题,但仍有一些其他问题。
What if the number in the string is not base 10? We can try to accommodate other bases by setting the stream to the correct mode (e.g. ss << std::hex
) before trying the conversion. But this means the caller must know a prioriwhat base the number is -- and how can the caller possibly know that? The caller doesn't know what the number is yet. They don't even know that it isa number! How can they be expected to know what base it is? We could just mandate that all numbers input to our programs must be base 10 and reject hexadecimal or octal input as invalid. But that is not very flexible or robust. There is no simple solution to this problem. You can't simply try the conversion once for each base, because the decimal conversion will always succeed for octal numbers (with a leading zero) and the octal conversion may succeed for some decimal numbers. So now you have to check for a leading zero. But wait! Hexadecimal numbers can start with a leading zero too (0x...). Sigh.
如果字符串中的数字不是以 10 为基数怎么办?ss << std::hex
在尝试转换之前,我们可以通过将流设置为正确的模式(例如)来尝试适应其他基础。但这意味着调用者必须先验地知道数字是什么基数——而调用者怎么可能知道呢?来电者还不知道号码是什么。他们甚至不知道这是一个号码!他们怎么能知道它是什么基地?我们可以强制要求所有输入到我们程序中的数字必须以 10 为基数,并拒绝十六进制或八进制输入无效。但这不是很灵活或健壮。这个问题没有简单的解决方案。您不能简单地为每个基数尝试一次转换,因为十进制转换对于八进制数(带有前导零)总是会成功,而对于某些十进制数,八进制转换可能会成功。所以现在你必须检查前导零。可是等等!十六进制数也可以以前导零开头(0x...)。叹。
Even if you succeed in dealing with the above problems, there is still another bigger problem: what if the caller needs to distinguish between bad input (e.g. "123foo") and a number that is out of the range of int
(e.g. "4000000000" for 32-bit int
)? With stringstream
, there is no way to make this distinction. We only know whether the conversion succeeded or failed. If it fails, we have no way of knowing whyit failed. As you can see, stringstream
leaves much to be desired if you want robustness and clear error handling.
即使你在处理上述问题取得成功,还有另一个更大的问题:如果主叫方需要坏输入(如“123foo”)和一个数字,范围的出区分int
(例如,“40亿”为32 位int
)?使用stringstream
,则无法进行这种区分。我们只知道转换是成功还是失败。如果它失败了,我们无法知道它失败的原因。如您所见,stringstream
如果您想要健壮性和清晰的错误处理,还有很多不足之处。
This leads me to my second piece of advice: do no use Boost's lexical_cast
for this. Consider what the lexical_cast
documentation has to say:
这引出了我的第二条建议:不要lexical_cast
为此使用 Boost。考虑一下lexical_cast
文档的内容:
Where a higher degree of control is required over conversions, std::stringstream and std::wstringstream offer a more appropriate path. Where non-stream-based conversions are required, lexical_cast is the wrong tool for the job and is not special-cased for such scenarios.
当需要对转换进行更高程度的控制时,std::stringstream 和 std::wstringstream 提供了更合适的路径。在需要非基于流的转换时, lexical_cast 是错误的工作工具,并且不是这种情况的特殊情况。
What?? We've already seen that stringstream
has a poor level of control, and yet it says stringstream
should be used instead of lexical_cast
if you need "a higher level of control". Also, because lexical_cast
is just a wrapper around stringstream
, it suffers from the same problems that stringstream
does: poor support for multiple number bases and poor error handling.
什么??我们已经看到它stringstream
的控制水平很差,但它说如果你需要“更高水平的控制” ,就stringstream
应该使用它lexical_cast
。此外,因为lexical_cast
它只是围绕 的包装器stringstream
,所以它会遇到与 相同的问题stringstream
:对多基数的支持差和错误处理差。
The best solution
最佳解决方案
Fortunately, somebody has already solved all of the above problems. The C standard library contains strtol
and family which have none of these problems.
幸运的是,已经有人解决了上述所有问题。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 == 'const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
' || *end != 'int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
') {
return INCONVERTIBLE;
}
i = l;
return SUCCESS;
}
Pretty simple for something that handles all the error cases and also supports any number base from 2 to 36. If base
is zero (the default) it will try to convert from any base. Or the caller can supply the third argument and specify that the conversion should only be attempted for a particular base. It is robust and handles all errors with a minimal amount of effort.
对于处理所有错误情况并且还支持从 2 到 36 的任何数字基数的东西来说非常简单。如果base
为零(默认值),它将尝试从任何基数转换。或者调用者可以提供第三个参数并指定只应针对特定基数尝试转换。它很健壮,可以用最少的努力处理所有错误。
Other reasons to prefer strtol
(and family):
喜欢strtol
(和家人)的其他原因:
- It exhibits much better runtime performance
- It introduces less compile-time overhead (the others pull in nearly 20 times more SLOC from headers)
- It results in the smallest code size
- 它表现出更好的运行时性能
- 它引入了更少的编译时开销(其他的从头文件中引入了近 20 倍的 SLOC)
- 它导致最小的代码大小
There is absolutely no good reason to use any other method.
绝对没有充分的理由使用任何其他方法。
回答by Luka Marinko
This is a safer C way than atoi()
这是比 atoi() 更安全的 C 方法
#include <boost/lexical_cast.hpp>
#include <string>
try
{
std::string str = "123";
int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
// Error
}
C++ with standard library stringstream: (thanks CMS)
带有标准库stringstream 的C++ :(感谢CMS)
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
With boostlibrary: (thanks jk)
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Edit: Fixed the stringstream version so that it handles errors. (thanks to CMS's and jk's comment on original post)
编辑:修复了 stringstream 版本,以便处理错误。(感谢 CMS 和 jk 对原始帖子的评论)
回答by Chris Arguin
The good 'old C way still works. I recommend strtol or strtoul. Between the return status and the 'endPtr', you can give good diagnostic output. It also handles multiple bases nicely.
好的'旧 C 方式仍然有效。我推荐 strtol 或 strtoul。在返回状态和“endPtr”之间,您可以提供良好的诊断输出。它还可以很好地处理多个碱基。
回答by jk.
You can use Boost's lexical_cast
, which wraps thisin a more generic interface.
lexical_cast<Target>(Source)
throws bad_lexical_cast
on failure.
您可以使用Boost'slexical_cast
,它将它包装在一个更通用的接口中。
lexical_cast<Target>(Source)
抛出bad_lexical_cast
失败。
回答by jk.
You can use the a stringstream from the C++ standard libraray:
您可以使用 C++ 标准库中的字符串流:
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<typename InputIterator, typename T>
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<T>(digit_table[static_cast<unsigned int>(*it++)]);
if (0xFF == digit)
return false;
v = (10 * v) + digit;
}
if (negative)
v *= -1;
return true;
}
The stream state will be set to fail if a non-digit is encountered when trying to read an integer.
如果在尝试读取整数时遇到非数字,则流状态将设置为失败。
See Stream pitfallsfor pitfalls of errorhandling and streams in C++.
有关C++ 中错误处理和流的陷阱,请参阅流陷阱。
回答by CMS
回答by CMS
The C++ String Toolkit Library (StrTk)has the following solution:
在C ++工具包字符串库(StrTk)具有以下溶液:
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;
}
The InputIterator can be of either unsigned char*, char* or std::string iterators, and T is expected to be a signed int, such as signed int, int, or long
InputIterator 可以是 unsigned char*、char* 或 std::string 迭代器,并且 T 应该是一个有符号 int,例如 signed int、int 或 long
回答by caa
I think these three links sum it up:
我认为这三个链接总结了它:
- 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
- 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 and lexical_cast solutions are about the same as lexical cast is using stringstream.
stringstream 和 lexical_cast 解决方案与使用 stringstream 的词法转换大致相同。
Some specializations of lexical cast use different approach see http://www.boost.org/doc/libs/release/boost/lexical_cast.hppfor details. Integers and floats are now specialized for integer to string conversion.
词法转换的一些专业使用不同的方法,详情请参见http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp。整数和浮点数现在专门用于整数到字符串的转换。
One can specialize lexical_cast for his/her own needs and make it fast. This would be the ultimate solution satisfying all parties, clean and simple.
可以根据自己的需要专门使用 lexical_cast 并使其快速。这将是满足所有各方的最终解决方案,干净而简单。
Articles already mentioned show comparison between different methods of converting integers <-> strings. Following approaches make sense: old c-way, spirit.karma, fastformat, simple naive loop.
已经提到的文章展示了转换整数 <-> 字符串的不同方法之间的比较。以下方法是有意义的:旧的 c-way、spirit.karma、fastformat、简单的朴素循环。
Lexical_cast is ok in some cases e.g. for int to string conversion.
Lexical_cast 在某些情况下是可以的,例如用于 int 到字符串的转换。
Converting string to int using lexical cast is not a good idea as it is 10-40 times slower than atoi depending on the platform/compiler used.
使用词法转换将字符串转换为 int 不是一个好主意,因为它比 atoi 慢 10-40 倍,具体取决于所使用的平台/编译器。
Boost.Spirit.Karma seems to be the fastest library for converting integer to string.
Boost.Spirit.Karma 似乎是将整数转换为字符串最快的库。
##代码##and basic simple loop from the article mentioned above is a fastest way to convert string to int, obviously not the safest one, strtol() seems like a safer solution
上面提到的文章中的基本简单循环是将字符串转换为 int 的最快方法,显然不是最安全的方法, strtol() 似乎是一个更安全的解决方案
##代码##回答by fuzzyTew
If you have C++11, the appropriate solutions nowadays are the C++ integer conversion functions in <string>
: stoi
, stol
, stoul
, stoll
, stoull
. They throw appropriate exceptions when given incorrect input and use the fast and small strto*
functions under the hood.
如果您有 C++11,那么现在合适的解决方案是 C++ 整数转换函数<string>
:stoi
, stol
, stoul
, stoll
, stoull
。当给出不正确的输入时,它们会抛出适当的异常,并在幕后使用快速和小strto*
功能。
If you are stuck with an earlier revision of C++, it would be forward-portable of you to mimic these functions in your implementation.
如果您坚持使用 C++ 的早期版本,那么在您的实现中模仿这些函数将是可移植的。