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

How to parse a string to an int in C++?

将字符串解析(以char *形式)转换成int的C++方式是什么?稳健和清晰的错误处理是一个优点(而不是返回零)。


不该做什么

这是我的第一条建议:不要使用Stringstream。虽然起初使用起来似乎很简单,但如果您希望健壮性和良好的错误处理,那么您将发现需要做很多额外的工作。

这是一种直观的方法,它看起来应该有效:

1
2
3
4
5
6
7
8
9
10
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中不再有字符来解决这个问题:

1
2
3
4
5
6
7
8
9
10
11
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范围的数字(如32位int)呢?对于stringstream来说,没有办法做出这样的区分。我们只知道转换是否成功。如果失败了,我们就无法知道它为什么会失败。如您所见,如果您希望健壮性和清晰的错误处理,那么stringstream将留下许多需要的地方。

这就引出了我的第二条建议:不要用Boost的lexical_cast来解决这个问题。考虑一下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.

什么??我们已经看到stringstream的控制水平很差,但是它说如果你需要"更高的控制水平",应该使用stringstream而不是lexical_cast。另外,由于lexical_cast只是stringstream的一个包装,它也面临着与stringstream相同的问题:对多个数基的支持差,错误处理差。

最佳解决方案

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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及家庭的其他原因:

  • 它显示出更好的运行时性能
  • 它引入的编译时间开销更少(其他的从头中引入的sloc比它多近20倍)
  • 它产生最小的代码大小

完全没有理由使用任何其他方法。


在新的C++ 11中,函数有:Stoi、Stol、Stol、Stoul等。

1
int myNr = std::stoi(myString);

它将在转换错误时引发异常。

即使是这些新函数,仍然存在与dan所指出的相同的问题:它们将很高兴地将字符串"11x"转换为整数"11"。

更多信息请参见:http://en.cppreference.com/w/cpp/string/basic_string/stol


这是比ATOI()更安全的C方法

1
2
3
4
5
6
7
const char* str ="123";
int i;

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

C++与标准库STRIGSTROW:(谢谢CMS)

1
2
3
4
5
6
7
8
9
int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  {
      //ERROR
  }
  return num;
}

带Boost库:(谢谢JK)

1
2
3
4
5
6
7
8
9
10
11
12
#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
}

编辑:修复了Stringstream版本,以便它处理错误。(感谢CMS和JK对原始帖子的评论)


您可以使用Boost的lexical_cast,它将此封装在一个更通用的接口中。lexical_cast(Source)失败时抛出bad_lexical_cast


好的"老C"方法仍然有效。我推荐Strtol或Strtoul。在返回状态和"endptr"之间,可以提供良好的诊断输出。它还可以很好地处理多个基地。


您可以使用C++标准库中的A股流:

1
2
3
4
5
6
7
8
9
stringstream ss(str);
int x;
ss >> x;

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

The stream state will be set to fail
if a non-digit is encountered when
trying to read an integer.

请参阅流错误的错误处理和流在C++中的陷阱。


可以使用Stringstream的

1
2
3
4
5
6
int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}


我认为这三个环节总结如下:

  • 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解决方案与Lexical-Cast使用Stringstream的解决方案大致相同。

词法转换的某些专门化使用不同的方法。有关详细信息,请参阅http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp。整数和浮点数现在专门用于整数到字符串的转换。

你可以根据自己的需要专门制作词汇表,并使之快速。这将是让各方满意的最终解决方案,既干净又简单。

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

词法转换在某些情况下是可以的,例如int到string转换。

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

karma似乎是将整数转换为字符串的最快库。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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++字符串工具包库(STRTK)具有以下解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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;
}

inputiTerator可以是无符号char*、char*或std::string迭代器,t应该是有符号int,如signed int、int或long。


如果你有C++ 11,现在适当的解决方案是EDCOX1中的C++整数转换函数:0:EDOCX1,1,EDOCX1,2,EDCOX1,3,EDCOX1,4,EDOCX1 5。当输入错误时,它们会抛出适当的异常,并使用发动机罩下的快速和小型strto*功能。

如果你被C++的早期修订所困扰,那么在你的实现中模拟这些函数将会是你的一种可移植性。


从C++ 17开始,您可以使用EDCOX1,0,EDCX1,1的头,如这里所记录的。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <charconv>
#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;
    }
}

作为一个额外的好处,它还可以处理其他基,比如十六进制。


我喜欢Dan Moulding的答案,我只想给它添加一点C++风格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

int to_int(const std::string &s, int base = 0)
{
    char *end;
    errno = 0;
    long result = std::strtol(s.c_str(), &end, base);
    if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
        throw std::out_of_range("toint: string is out of range");
    if (s.length() == 0 || *end != '\0')
        throw std::invalid_argument("toint: invalid string");
    return result;
}

它通过隐式转换同时适用于std::string和const char*。它还可用于基础转换,例如,所有to_int("0x7b")to_int("0173")to_int("01111011", 2)以及to_int("0000007B", 16)to_int("11120", 3)to_int("3L", 34);将返回123。

std::stoi不同,它在pre-c++11中工作。与std::stoiboost::lexical_caststringstream不同的是,它对"123hohoho"这样的奇怪字符串也有例外。

注意:此函数允许前导空格而不允许尾随空格,即to_int(" 123")返回123,to_int("123 ")抛出异常。确保这对于您的用例是可接受的,或者调整代码。

这种功能可能是STL的一部分。


我知道将字符串转换为int的三种方法:

要么使用stoi(string to int)函数,要么只使用stringstream,这是进行单个转换的第三种方法,代码如下:

第一方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::string s1 ="4533";
std::string s2 ="3.010101";
std::string s3 ="31337 with some string";

int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);

std::cout <<  s1 <<"=" << myint1 << '
'
;
std::cout <<  s2 <<"=" << myint2 << '
'
;
std::cout <<  s3 <<"=" << myint3 << '
'
;

第二方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;


int StringToInteger(string NumberAsString)
{
    int NumberAsInteger;
    stringstream ss;
    ss << NumberAsString;
    ss >> NumberAsInteger;
    return NumberAsInteger;
}
int main()
{
    string NumberAsString;
    cin >> NumberAsString;
    cout << StringToInteger(NumberAsString) << endl;
    return 0;
}

第三种方法-但不适用于单个转换

1
2
3
4
5
6
7
8
9
std::string str4 ="453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{

    in = str4[i];
    cout <<in-48 ;

}

我喜欢丹的回答,尤其是因为避免了例外。对于嵌入式系统开发和其他低级系统开发,可能没有合适的异常框架可用。

在有效字符串后添加了一个空白检查…这三行

1
2
3
    while (isspace(*end)) {
        end++;
    }

还添加了分析错误的检查。

1
2
3
    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }

这是完整的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

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

STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
    char *end = (char *)s;
    errno = 0;

    l = strtol(s, &end, base);

    if ((errno == ERANGE) && (l == LONG_MAX)) {
        return OVERFLOW;
    }
    if ((errno == ERANGE) && (l == LONG_MIN)) {
        return UNDERFLOW;
    }
    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }    
    while (isspace((unsigned char)*end)) {
        end++;
    }

    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }

    return SUCCESS;
}


我知道这是一个老问题,但我已经遇到过很多次了,到目前为止,还没有找到一个具有以下特征的模板化解决方案:

  • 可以转换任何基(并检测基类型)
  • 将检测错误的数据(即,确保整个字符串(少于前导/尾随空格)被转换消耗)
  • 将确保,无论转换为何种类型,字符串值的范围都是可接受的。

所以,这是我的,带着测试带。因为它在引擎盖下使用C函数strtoull/strtoll,所以它总是首先转换为可用的最大类型。然后,如果您没有使用最大的类型,它将执行额外的范围检查,以验证您的类型没有溢出(不足)。因此,它的性能比正确选择strtol/strtoul要差一点。但是,它也适用于short/chars,据我所知,也没有标准的库函数可以做到这一点。

享受;希望有人发现它有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>

static const int DefaultBase = 10;

template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
    while (isspace(*str)) str++; // remove leading spaces; verify there's data
    if (*str == '\0') { throw std::invalid_argument("str; no data"); } // nothing to convert

    // NOTE:  for some reason strtoull allows a negative sign, we don't; if
    //          converting to an unsigned then it must always be positive!
    if (!std::numeric_limits<T>::is_signed && *str == '-')
    { throw std::invalid_argument("str; negative"); }

    // reset errno and call fn (either strtoll or strtoull)
    errno = 0;
    char *ePtr;
    T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
                                              : strtoull(str, &ePtr, base);

    // check for any C errors -- note these are range errors on T, which may
    //   still be out of the range of the actual type we're using; the caller
    //   may need to perform additional range checks.
    if (errno != 0)
    {
            if (errno == ERANGE) { throw std::range_error("str; out of range"); }
            else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
            else { throw std::invalid_argument("str; unknown errno"); }
    }

    // verify everything converted -- extraneous spaces are allowed
    if (ePtr != NULL)
    {
            while (isspace(*ePtr)) ePtr++;
            if (*ePtr != '\0') { throw std::invalid_argument("str; bad data"); }
    }

    return tmp;
}

template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
    static const long long max = std::numeric_limits<T>::max();
    static const long long min = std::numeric_limits<T>::min();

    long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp < min || tmp > max)
    {
            std::ostringstream err;
            err <<"str; value" << tmp <<" out of" << sizeof(T) * 8
                <<"-bit signed range (";
            if (sizeof(T) != 1) err << min <<".." << max;
            else err << (int) min <<".." << (int) max;  // don't print garbage chars
            err <<")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
    static const unsigned long long max = std::numeric_limits<T>::max();

    unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp > max)
    {
            std::ostringstream err;
            err <<"str; value" << tmp <<" out of" << sizeof(T) * 8
                <<"-bit unsigned range (0..";
            if (sizeof(T) != 1) err << max;
            else err << (int) max;  // don't print garbage chars
            err <<")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
    return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
                                             : StringToUnsigned<T>(str, base);
}

template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
    return out_convertedVal = StringToDecimal<T>(str, base);
}

/*============================== [ Test Strap ] ==============================*/

#include <inttypes.h>
#include <iostream>

static bool _g_anyFailed = false;

template<typename T>
void TestIt(const char *tName,
            const char *s, int base,
            bool successExpected = false, T expectedValue = 0)
{
    #define FAIL(s) { _g_anyFailed = true; std::cout << s; }

    T x;
    std::cout <<"converting<" << tName <<">b:" << base <<" [" << s <<"]";
    try
    {
            StringToDecimal<T>(x, s, base);
            // get here on success only
            if (!successExpected)
            {
                    FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
            }
            else
            {
                    std::cout <<" ->";
                    if (sizeof(T) != 1) std::cout << x;
                    else std::cout << (int) x;  // don't print garbage chars
                    if (x != expectedValue)
                    {
                            FAIL("; FAILED (expected value:" << expectedValue <<")!");
                    }
                    std::cout << std::endl;
            }
    }
    catch (std::exception &e)
    {
            if (successExpected)
            {
                    FAIL(  " -- TEST FAILED; EXPECTED SUCCESS!"
                         <<" (got:" << e.what() <<")" << std::endl);
            }
            else
            {
                    std::cout <<"; expected exception encounterd: [" << e.what() <<"]" << std::endl;
            }
    }
}

#define TEST(t, s, ...) \
    TestIt<t>(#t, s, __VA_ARGS__);


int main()
{
    std::cout <<"============ variable base tests ============" << std::endl;
    TEST(int,"-0xF", 0, true, -0xF);
    TEST(int,"+0xF", 0, true, 0xF);
    TEST(int,"0xF", 0, true, 0xF);
    TEST(int,"-010", 0, true, -010);
    TEST(int,"+010", 0, true, 010);
    TEST(int,"010", 0, true, 010);
    TEST(int,"-10", 0, true, -10);
    TEST(int,"+10", 0, true, 10);
    TEST(int,"10", 0, true, 10);

    std::cout <<"============ base-10 tests ============" << std::endl;
    TEST(int,"-010", 10, true, -10);
    TEST(int,"+010", 10, true, 10);
    TEST(int,"010", 10, true, 10);
    TEST(int,"-10", 10, true, -10);
    TEST(int,"+10", 10, true, 10);
    TEST(int,"10", 10, true, 10);
    TEST(int,"00010", 10, true, 10);

    std::cout <<"============ base-8 tests ============" << std::endl;
    TEST(int,"777", 8, true, 0777);
    TEST(int,"-0111", 8, true, -0111);
    TEST(int,"+0010", 8, true, 010);

    std::cout <<"============ base-16 tests ============" << std::endl;
    TEST(int,"DEAD", 16, true, 0xDEAD);
    TEST(int,"-BEEF", 16, true, -0xBEEF);
    TEST(int,"+C30", 16, true, 0xC30);

    std::cout <<"============ base-2 tests ============" << std::endl;
    TEST(int,"-10011001", 2, true, -153);
    TEST(int,"10011001", 2, true, 153);

    std::cout <<"============ irregular base tests ============" << std::endl;
    TEST(int,"Z", 36, true, 35);
    TEST(int,"ZZTOP", 36, true, 60457993);
    TEST(int,"G", 17, true, 16);
    TEST(int,"H", 17);

    std::cout <<"============ space deliminated tests ============" << std::endl;
    TEST(int,"1337   ", 10, true, 1337);
    TEST(int,"   FEAD", 16, true, 0xFEAD);
    TEST(int,"   0711  ", 0, true, 0711);

    std::cout <<"============ bad data tests ============" << std::endl;
    TEST(int,"FEAD", 10);
    TEST(int,"1234 asdfklj", 10);
    TEST(int,"-0xF", 10);
    TEST(int,"+0xF", 10);
    TEST(int,"0xF", 10);
    TEST(int,"-F", 10);
    TEST(int,"+F", 10);
    TEST(int,"12.4", 10);
    TEST(int,"ABG", 16);
    TEST(int,"10011002", 2);

    std::cout <<"============ int8_t range tests ============" << std::endl;
    TEST(int8_t,"7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(int8_t,"80", 16);
    TEST(int8_t,"-80", 16, true, std::numeric_limits<int8_t>::min());
    TEST(int8_t,"-81", 16);
    TEST(int8_t,"FF", 16);
    TEST(int8_t,"100", 16);

    std::cout <<"============ uint8_t range tests ============" << std::endl;
    TEST(uint8_t,"7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(uint8_t,"80", 16, true, std::numeric_limits<int8_t>::max()+1);
    TEST(uint8_t,"-80", 16);
    TEST(uint8_t,"-81", 16);
    TEST(uint8_t,"FF", 16, true, std::numeric_limits<uint8_t>::max());
    TEST(uint8_t,"100", 16);

    std::cout <<"============ int16_t range tests ============" << std::endl;
    TEST(int16_t,"7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(int16_t,"8000", 16);
    TEST(int16_t,"-8000", 16, true, std::numeric_limits<int16_t>::min());
    TEST(int16_t,"-8001", 16);
    TEST(int16_t,"FFFF", 16);
    TEST(int16_t,"10000", 16);

    std::cout <<"============ uint16_t range tests ============" << std::endl;
    TEST(uint16_t,"7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(uint16_t,"8000", 16, true, std::numeric_limits<int16_t>::max()+1);
    TEST(uint16_t,"-8000", 16);
    TEST(uint16_t,"-8001", 16);
    TEST(uint16_t,"FFFF", 16, true, std::numeric_limits<uint16_t>::max());
    TEST(uint16_t,"10000", 16);

    std::cout <<"============ int32_t range tests ============" << std::endl;
    TEST(int32_t,"7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(int32_t,"80000000", 16);
    TEST(int32_t,"-80000000", 16, true, std::numeric_limits<int32_t>::min());
    TEST(int32_t,"-80000001", 16);
    TEST(int32_t,"FFFFFFFF", 16);
    TEST(int32_t,"100000000", 16);

    std::cout <<"============ uint32_t range tests ============" << std::endl;
    TEST(uint32_t,"7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(uint32_t,"80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
    TEST(uint32_t,"-80000000", 16);
    TEST(uint32_t,"-80000001", 16);
    TEST(uint32_t,"FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
    TEST(uint32_t,"100000000", 16);

    std::cout <<"============ int64_t range tests ============" << std::endl;
    TEST(int64_t,"7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(int64_t,"8000000000000000", 16);
    TEST(int64_t,"-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
    TEST(int64_t,"-8000000000000001", 16);
    TEST(int64_t,"FFFFFFFFFFFFFFFF", 16);
    TEST(int64_t,"10000000000000000", 16);

    std::cout <<"============ uint64_t range tests ============" << std::endl;
    TEST(uint64_t,"7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(uint64_t,"8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
    TEST(uint64_t,"-8000000000000000", 16);
    TEST(uint64_t,"-8000000000000001", 16);
    TEST(uint64_t,"FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
    TEST(uint64_t,"10000000000000000", 16);

    std::cout << std::endl << std::endl
              << (_g_anyFailed ?"!! SOME TESTS FAILED !!" :"ALL TESTS PASSED")
              << std::endl;

    return _g_anyFailed;
}

StringToDecimal是user-land方法;它是重载的,因此可以这样调用它:

1
int a; a = StringToDecimal<int>("100");

或者:

1
int a; StringToDecimal(a,"100");

我讨厌重复int类型,所以更喜欢后者。这样可以确保,如果"a"的类型发生更改,不会得到坏的结果。我希望编译器能像这样理解它:

1
int a; a = StringToDecimal("100");

但是,C++没有演绎模板返回类型,所以这是我能得到的最好的。

实现非常简单:

CstrtoxllWrapperstrtoullstrtoll包装起来,根据模板类型的有符号性调用必要的内容,并提供一些附加保证(例如,如果无符号,则不允许负输入,并且它确保整个字符串被转换)。

CstrtoxllWrapperStringToSignedStringToUnsigned使用,编译器可以使用最大的类型(long long/unsigned long long);这允许执行最大的转换。然后,如果需要,StringToSigned/StringToUnsigned对基础类型执行最终范围检查。最后,端点方法StringToDecimal根据基础类型的符号性决定要调用的StringTo*模板方法中的哪一个。

我认为大多数垃圾都可以通过编译器进行优化;几乎所有东西都应该是编译时确定性的。任何关于这方面的评论对我来说都是有趣的!


您可以使用这个定义的方法。

1
#define toInt(x) {atoi(x.c_str())};

如果要将字符串转换为整数,只需执行以下操作。

1
2
3
4
5
6
7
int main()
{
string test ="46", test2 ="56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

输出为102。


在C语言中,可以使用int atoi (const char * str)

解析C-string str,将其内容解释为整数,该整数作为int类型的值返回。