如何在c++中解析字符串到int ?

时间:2022-01-11 16:37:42

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++方法是什么?健壮和清晰的错误处理是一个附加的(而不是返回零)。

17 个解决方案

#1


133  

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 issue as 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

#2


184  

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,我将得到值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 priori what 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 is a 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 why it failed. As you can see, stringstream leaves much to be desired if you want robustness and clear error handling.

即使您成功地处理了上述问题,还有一个更大的问题:如果调用者需要区分错误的输入(例如:“123foo”)和一个超出整数范围的数字(例如:“4000000000”的32位整数)?有了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:

这就引出了我的第二条建议:不要使用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.

在转换过程中需要更高的控制级别时,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和family。

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;
}

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):

更喜欢斯特拉尔(和家人)的其他理由:

  • It exhibits much better runtime performance
  • 它展示了更好的运行时性能。
  • It introduces less compile-time overhead (the others pull in nearly 20 times more SLOC from headers)
  • 它引入了更少的编译时开销(其他的从标题中引入了近20倍的SLOC)
  • It results in the smallest code size
  • 它会导致最小的代码大小。

There is absolutely no good reason to use any other method.

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

#3


65  

This is a safer C way than atoi()

这比atoi()更安全

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

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

C++ with standard library stringstream: (thanks CMS )

C++标准库stringstream:(谢谢CMS)

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

With boost library: (thanks jk)

与boost库:(感谢jk)

#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
}

Edit: Fixed the stringstream version so that it handles errors. (thanks to CMS's and jk's comment on original post)

编辑:修复stringstream版本,以便处理错误。(感谢CMS和jk对原创文章的评论)

#4


22  

You can use Boost's lexical_cast, which wraps this in a more generic interface. lexical_cast<Target>(Source) throws bad_lexical_cast on failure.

您可以使用Boost的lexical_cast,它将其封装在一个更通用的接口中。lexical_cast <目标> (源)抛出bad_lexical_cast失败。

#5


20  

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”之间,您可以提供良好的诊断输出。它还能很好地处理多个碱基。

#6


15  

You can use the a stringstream from the C++ standard libraray:

您可以使用来自c++标准libraray的stringstream:

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.

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

See Stream pitfalls for pitfalls of errorhandling and streams in C++.

在c++中看到错误处理和流的陷阱的流陷阱。

#7


9  

You can use stringstream's

您可以使用stringstream

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

#8


8  

The C++ String Toolkit Library (StrTk) has the following solution:

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<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 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可以是无符号的char*、char*或std::string迭代器,而T是一个有符号的int类型,例如符号int、int或long。

#9


7  

I think these three links sum it up:

我认为这三个环节总结起来:

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.hpp for 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道,精神。业力,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.

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

Boost.Spirit.Karma seems to be the fastest library for converting integer to string.

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

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

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()似乎是一个更安全的解决方案。

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


5  

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,那么现在合适的解决方案是在 : stoi、stol、stoul、stoll、stoull中使用c++整数转换函数。当给出错误的输入并使用快速和小的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++的早期版本,那么在您的实现中模拟这些函数将是具有前瞻性的。

#11


2  

I like Dan Moulding's answer, I'll just add a bit of C++ style to it:

我喜欢Dan Moulding的答案,我只需要加上一点c++的风格:

#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;
}

It works for both std::string and const char* through the implicit conversion. It's also useful for base conversion, e.g. all to_int("0x7b") and to_int("0173") and to_int("01111011", 2) and to_int("0000007B", 16) and to_int("11120", 3) and to_int("3L", 34); would return 123.

它适用于std::字符串和const char*通过隐式转换。它对于基础转换也很有用,例如:all to_int(“0x7b”)和to_int(“0173”)和to_int(“01111011”,2)和to_int(“0000007B”,16)和to_int(“11120”,3)和to_int(“3L”,34);将返回123。

Unlike std::stoi it works in pre-C++11. Also unlike std::stoi, boost::lexical_cast and stringstream it throws exceptions for weird strings like "123hohoho".

与std不同:stoi在pre- c++ 11中工作。也不像std::stoi, boost::lexical_cast和stringstream它会抛出异常字符串,比如“123hohoho”。

NB: This function tolerates leading spaces but not trailing spaces, i.e. to_int(" 123") returns 123 while to_int("123 ") throws exception. Make sure this is acceptable for your use case or adjust the code.

NB:这个函数可以容忍领先的空格,但不是拖尾空格,即to_int(“123”)返回123,而to_int(“123”)则抛出异常。确保您的用例可以接受,或者调整代码。

Such function could be part of STL...

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

#12


2  

I know three ways of converting String into int:

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

Either use stoi(String to int) function or just go with Stringstream, the third way to go individual conversion, Code is below:

要么使用stoi(String to int)函数,要么直接使用Stringstream,这是实现单个转换的第三种方法,代码如下:

1st Method

1号方法

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 << '\n';
std::cout <<  s2 <<"=" << myint2 << '\n';
std::cout <<  s3 <<"=" << myint3 << '\n';

2nd Method

2方法

#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;
} 

3rd Method - but not for an individual conversion

第三种方法——但不是针对个人的转换。

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 ;

}

#13


1  

I like Dan's answer, esp because of the avoidance of exceptions. For embedded systems development and other low level system development, there may not be a proper Exception framework available.

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

Added a check for white-space after a valid string...these three lines

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

    while (isspace(*end)) {
        end++;
    }


Added a check for parsing errors too.

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

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


Here is the complete function..

这是完整的函数。

#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;
}

#14


1  

From C++17 onwards you can use std::from_chars from the <charconv> header as documented here.

从c++ 17开始,您可以使用std::from_chars,从 头文件中记录。

For example:

例如:

#include <iostream>
#include <charconv>
#include <array>

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

    std::from_chars_result result = std::from_chars(begin(str), 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;
    }
}

As a bonus, it can also handle other bases, like hexadecimal.

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

#15


0  

You could use this defined method.

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

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

And if you were to convert from String to an Integer, you would just do the following.

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

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

The output would be 102.

输出是102。

#16


0  

I know this is an older question, but I've come across it so many times and, to date, have still not found a nicely templated solution having the following characteristics:

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

  • Can convert any base (and detect base type)
  • 可以转换任何基础(并检测基本类型)
  • Will detect erroneous data (i.e. ensure the entire string, less leading/trailing whitespace, is consumed by the conversion)
  • 将检测错误数据(即确保整个字符串、更少的领先/尾随空格,被转换消耗)
  • Will ensure that, regardless of the type converted to, the range of the string's value is acceptable.
  • 将确保无论类型转换为何种类型,字符串的值的范围都是可以接受的。

So, here is mine, with a test strap. Because it uses the C functions strtoull/strtoll under the hood, it always converts first to the largest type available. Then, if you are not using the largest type, it will perform additional range checks to verify your type was not over(under)flowed. For this, it is a little less performant than if one properly chose strtol/strtoul. However, it also works for shorts/chars and, to the best of my knowledge, there exists no standard library function that does that, too.

这是我的,带一个测试带。因为它使用的是在引擎盖下的C函数,它总是首先转换为可用的最大类型。然后,如果您没有使用最大的类型,它将执行额外的范围检查,以验证您的类型是否已溢出(下)。对于这一点,它比正确选择strtol/strtoul的效果要差一些。然而,它也适用于短片/图表,而且,据我所知,没有标准的库函数也能做到这一点。

Enjoy; hopefully someone finds it useful.

享受;希望有人觉得它有用。

#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 is the user-land method; it is overloaded so it can be called either like this:

StringToDecimal是用户土地方法;它是重载的,所以可以这样调用:

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

or this:

或:

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

I hate repeating the int type, so prefer the latter. This ensures that if the type of 'a' changes one does not get bad results. I wish the compiler could figure it out like:

我讨厌重复int类型,所以喜欢后者。这确保了如果“a”类型的更改不会产生不好的结果。我希望编译器能像:

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

...but, C++ does not deduce template return types, so that's the best I can get.

…但是,c++并没有推出模板返回类型,所以这是我能得到的最好的结果。

The implementation is pretty simple:

实现非常简单:

CstrtoxllWrapper wraps both strtoull and strtoll, calling whichever is necessary based on the template type's signed-ness and providing some additional guarantees (e.g. negative input is disallowed if unsigned and it ensures the entire string was converted).

CstrtoxllWrapper封装了strtoull和strtoll,调用了基于模板类型的signed的必要条件,并提供了一些额外的保证(例如,如果没有签名,则不允许输入负数,并且保证了整个字符串的转换)。

CstrtoxllWrapper is used by StringToSigned and StringToUnsigned with the largest type (long long/unsigned long long) available to the compiler; this allows the maximal conversion to be performed. Then, if it is necessary, StringToSigned/StringToUnsigned performs the final range checks on the underlying type. Finally, the end-point method, StringToDecimal, decides which of the StringTo* template methods to call based on the underlying type's signed-ness.

CstrtoxllWrapper被StringToSigned和StringToUnsigned使用,它是编译器可以使用的最大类型(long long/unsigned long long)。这允许执行最大的转换。然后,如果有必要,StringToSigned/StringToUnsigned对底层类型执行最后的范围检查。最后,端点方法,StringToDecimal,决定了基于底层类型的签名的StringTo*模板方法中的哪一个。

I think most of the junk can be optimized out by the compiler; just about everything should be compile-time deterministic. Any commentary on this aspect would be interesting to me!

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

#17


-2  

In C, you can use int atoi (const char * str),

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

Parses the C-string str interpreting its content as an integral number, which is returned as a value of type int.

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

#1


133  

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 issue as 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

#2


184  

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,我将得到值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 priori what 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 is a 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 why it failed. As you can see, stringstream leaves much to be desired if you want robustness and clear error handling.

即使您成功地处理了上述问题,还有一个更大的问题:如果调用者需要区分错误的输入(例如:“123foo”)和一个超出整数范围的数字(例如:“4000000000”的32位整数)?有了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:

这就引出了我的第二条建议:不要使用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.

在转换过程中需要更高的控制级别时,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和family。

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;
}

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):

更喜欢斯特拉尔(和家人)的其他理由:

  • It exhibits much better runtime performance
  • 它展示了更好的运行时性能。
  • It introduces less compile-time overhead (the others pull in nearly 20 times more SLOC from headers)
  • 它引入了更少的编译时开销(其他的从标题中引入了近20倍的SLOC)
  • It results in the smallest code size
  • 它会导致最小的代码大小。

There is absolutely no good reason to use any other method.

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

#3


65  

This is a safer C way than atoi()

这比atoi()更安全

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

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

C++ with standard library stringstream: (thanks CMS )

C++标准库stringstream:(谢谢CMS)

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

With boost library: (thanks jk)

与boost库:(感谢jk)

#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
}

Edit: Fixed the stringstream version so that it handles errors. (thanks to CMS's and jk's comment on original post)

编辑:修复stringstream版本,以便处理错误。(感谢CMS和jk对原创文章的评论)

#4


22  

You can use Boost's lexical_cast, which wraps this in a more generic interface. lexical_cast<Target>(Source) throws bad_lexical_cast on failure.

您可以使用Boost的lexical_cast,它将其封装在一个更通用的接口中。lexical_cast <目标> (源)抛出bad_lexical_cast失败。

#5


20  

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”之间,您可以提供良好的诊断输出。它还能很好地处理多个碱基。

#6


15  

You can use the a stringstream from the C++ standard libraray:

您可以使用来自c++标准libraray的stringstream:

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.

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

See Stream pitfalls for pitfalls of errorhandling and streams in C++.

在c++中看到错误处理和流的陷阱的流陷阱。

#7


9  

You can use stringstream's

您可以使用stringstream

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

#8


8  

The C++ String Toolkit Library (StrTk) has the following solution:

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<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 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可以是无符号的char*、char*或std::string迭代器,而T是一个有符号的int类型,例如符号int、int或long。

#9


7  

I think these three links sum it up:

我认为这三个环节总结起来:

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.hpp for 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道,精神。业力,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.

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

Boost.Spirit.Karma seems to be the fastest library for converting integer to string.

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

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

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()似乎是一个更安全的解决方案。

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


5  

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,那么现在合适的解决方案是在 : stoi、stol、stoul、stoll、stoull中使用c++整数转换函数。当给出错误的输入并使用快速和小的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++的早期版本,那么在您的实现中模拟这些函数将是具有前瞻性的。

#11


2  

I like Dan Moulding's answer, I'll just add a bit of C++ style to it:

我喜欢Dan Moulding的答案,我只需要加上一点c++的风格:

#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;
}

It works for both std::string and const char* through the implicit conversion. It's also useful for base conversion, e.g. all to_int("0x7b") and to_int("0173") and to_int("01111011", 2) and to_int("0000007B", 16) and to_int("11120", 3) and to_int("3L", 34); would return 123.

它适用于std::字符串和const char*通过隐式转换。它对于基础转换也很有用,例如:all to_int(“0x7b”)和to_int(“0173”)和to_int(“01111011”,2)和to_int(“0000007B”,16)和to_int(“11120”,3)和to_int(“3L”,34);将返回123。

Unlike std::stoi it works in pre-C++11. Also unlike std::stoi, boost::lexical_cast and stringstream it throws exceptions for weird strings like "123hohoho".

与std不同:stoi在pre- c++ 11中工作。也不像std::stoi, boost::lexical_cast和stringstream它会抛出异常字符串,比如“123hohoho”。

NB: This function tolerates leading spaces but not trailing spaces, i.e. to_int(" 123") returns 123 while to_int("123 ") throws exception. Make sure this is acceptable for your use case or adjust the code.

NB:这个函数可以容忍领先的空格,但不是拖尾空格,即to_int(“123”)返回123,而to_int(“123”)则抛出异常。确保您的用例可以接受,或者调整代码。

Such function could be part of STL...

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

#12


2  

I know three ways of converting String into int:

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

Either use stoi(String to int) function or just go with Stringstream, the third way to go individual conversion, Code is below:

要么使用stoi(String to int)函数,要么直接使用Stringstream,这是实现单个转换的第三种方法,代码如下:

1st Method

1号方法

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 << '\n';
std::cout <<  s2 <<"=" << myint2 << '\n';
std::cout <<  s3 <<"=" << myint3 << '\n';

2nd Method

2方法

#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;
} 

3rd Method - but not for an individual conversion

第三种方法——但不是针对个人的转换。

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 ;

}

#13


1  

I like Dan's answer, esp because of the avoidance of exceptions. For embedded systems development and other low level system development, there may not be a proper Exception framework available.

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

Added a check for white-space after a valid string...these three lines

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

    while (isspace(*end)) {
        end++;
    }


Added a check for parsing errors too.

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

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


Here is the complete function..

这是完整的函数。

#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;
}

#14


1  

From C++17 onwards you can use std::from_chars from the <charconv> header as documented here.

从c++ 17开始,您可以使用std::from_chars,从 头文件中记录。

For example:

例如:

#include <iostream>
#include <charconv>
#include <array>

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

    std::from_chars_result result = std::from_chars(begin(str), 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;
    }
}

As a bonus, it can also handle other bases, like hexadecimal.

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

#15


0  

You could use this defined method.

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

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

And if you were to convert from String to an Integer, you would just do the following.

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

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

The output would be 102.

输出是102。

#16


0  

I know this is an older question, but I've come across it so many times and, to date, have still not found a nicely templated solution having the following characteristics:

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

  • Can convert any base (and detect base type)
  • 可以转换任何基础(并检测基本类型)
  • Will detect erroneous data (i.e. ensure the entire string, less leading/trailing whitespace, is consumed by the conversion)
  • 将检测错误数据(即确保整个字符串、更少的领先/尾随空格,被转换消耗)
  • Will ensure that, regardless of the type converted to, the range of the string's value is acceptable.
  • 将确保无论类型转换为何种类型,字符串的值的范围都是可以接受的。

So, here is mine, with a test strap. Because it uses the C functions strtoull/strtoll under the hood, it always converts first to the largest type available. Then, if you are not using the largest type, it will perform additional range checks to verify your type was not over(under)flowed. For this, it is a little less performant than if one properly chose strtol/strtoul. However, it also works for shorts/chars and, to the best of my knowledge, there exists no standard library function that does that, too.

这是我的,带一个测试带。因为它使用的是在引擎盖下的C函数,它总是首先转换为可用的最大类型。然后,如果您没有使用最大的类型,它将执行额外的范围检查,以验证您的类型是否已溢出(下)。对于这一点,它比正确选择strtol/strtoul的效果要差一些。然而,它也适用于短片/图表,而且,据我所知,没有标准的库函数也能做到这一点。

Enjoy; hopefully someone finds it useful.

享受;希望有人觉得它有用。

#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 is the user-land method; it is overloaded so it can be called either like this:

StringToDecimal是用户土地方法;它是重载的,所以可以这样调用:

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

or this:

或:

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

I hate repeating the int type, so prefer the latter. This ensures that if the type of 'a' changes one does not get bad results. I wish the compiler could figure it out like:

我讨厌重复int类型,所以喜欢后者。这确保了如果“a”类型的更改不会产生不好的结果。我希望编译器能像:

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

...but, C++ does not deduce template return types, so that's the best I can get.

…但是,c++并没有推出模板返回类型,所以这是我能得到的最好的结果。

The implementation is pretty simple:

实现非常简单:

CstrtoxllWrapper wraps both strtoull and strtoll, calling whichever is necessary based on the template type's signed-ness and providing some additional guarantees (e.g. negative input is disallowed if unsigned and it ensures the entire string was converted).

CstrtoxllWrapper封装了strtoull和strtoll,调用了基于模板类型的signed的必要条件,并提供了一些额外的保证(例如,如果没有签名,则不允许输入负数,并且保证了整个字符串的转换)。

CstrtoxllWrapper is used by StringToSigned and StringToUnsigned with the largest type (long long/unsigned long long) available to the compiler; this allows the maximal conversion to be performed. Then, if it is necessary, StringToSigned/StringToUnsigned performs the final range checks on the underlying type. Finally, the end-point method, StringToDecimal, decides which of the StringTo* template methods to call based on the underlying type's signed-ness.

CstrtoxllWrapper被StringToSigned和StringToUnsigned使用,它是编译器可以使用的最大类型(long long/unsigned long long)。这允许执行最大的转换。然后,如果有必要,StringToSigned/StringToUnsigned对底层类型执行最后的范围检查。最后,端点方法,StringToDecimal,决定了基于底层类型的签名的StringTo*模板方法中的哪一个。

I think most of the junk can be optimized out by the compiler; just about everything should be compile-time deterministic. Any commentary on this aspect would be interesting to me!

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

#17


-2  

In C, you can use int atoi (const char * str),

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

Parses the C-string str interpreting its content as an integral number, which is returned as a value of type int.

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