如何parsing一个string在C + +中的int?

什么是parsing一个string(作为char *)给一个int的C ++方式? 强大和清晰的error handling是一个加号(而不是返回零 )。

在新的C ++ 11中有这样的function:stoi,stol,stoll,stoul等等。

int myNr = std::stoi(myString); 

它会抛出转换错误的exception。

即使这些新function仍然 Dan所指出的问题一样:他们将愉快地将string“11x”转换为整数“11”。

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

什么不该做

这是我的第一条build议: 不要为此使用stringstream 。 虽然起初看起来使用起来很简单,但是如果你想要健壮性和良好的error handling,你会发现你需要做很多额外的工作。

这是一种直觉上似乎应该起作用的方法:

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

这有一个主要问题: str2int(i, "1337h4x0r")将愉快地返回truei会得到值1337 。 我们可以通过确保转换后的stringstream中不再有字符来解决这个问题:

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

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

如果string中的数字不是10? 在尝试转换之前,我们可以尝试通过将stream设置为正确的模式(例如, ss << std::hex )来适应其他基础。 但是这意味着主叫方必须先知道该号码是以什么为基础的 – 主叫方怎么可能知道? 来电者不知道该号码是什么。 他们甚至不知道这一个数字! 他们怎么能期望知道它是什么基础? 我们可以强制input到我们的程序中的所有数字必须以10为基数,并将hex或八进制input拒绝为无效。 但是这不是非常灵活或强大。 这个问题没有简单的解决办法。 您不能简单地尝试一次转换为每个基地,因为十进制转换将始终成功的八进制数字(前导零)和八进制转换可能成功的一些十进制数字。 所以现在你必须检查一个前导零。 可是等等! hex数字也可以从前导零开始(0x …)。 叹。

即使你成功地处理了上述问题,还有一个更大的问题:如果调用者需要区分不好的input(例如“123foo”)和超出int范围的数字(例如“4000000000”为32位int )? 使用stringstream ,没有办法做出这种区分。 我们只知道转换是成功还是失败。 如果失败了,我们无法知道失败的原因 。 正如你所看到的,如果你想要健壮性和清晰的error handling, stringstream很多不足之处。

这引出了我的第二条build议: 不要使用Boost的lexical_cast 。 考虑一下lexical_cast文档必须说的:

如果转换需要更高程度的控制,则std :: stringstream和std :: wstringstream会提供更合适的path。 在不需要基于stream的转换的情况下,lexical_cast是工作的错误工具,对于这种情况并不是特别的。

什么?? 我们已经看到, stringstream的控制水平很差,但是如果你需要“更高级别的控制”,它应该使用stringstream而不是lexical_cast 。 另外,因为lexical_cast只是stringstream一个包装,所以也会遇到与stringstream相同的问题:对多个数字库的支持不足,error handling较差。

最好的解决scheme

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

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

对于处理所有错误情况的东西非常简单,并且还支持从2到36的任何数字基数。如果base为零(默认值),它将尝试从任何基数进行转换。 或者,调用者可以提供第三个参数,并指定只应该为特定的基础尝试转换。 它是健壮的,并以最小的努力处理所有的错误。

其他原因更喜欢strtol (和家庭):

  • 它展现了更好的运行时性能
  • 它引入了更less的编译时间开销(其他的从头文件中抽取了将近20倍的SLOC)
  • 它导致最小的代码大小

使用任何其他方法绝对没有理由。

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

 const char* str = "123"; int i; if(sscanf(str, "%d", &i) == EOF ) { /* error */ } 

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

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

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

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

您可以使用Boost的lexical_cast ,将其包装在更通用的界面中。 lexical_cast<Target>(Source)在失败时抛出bad_lexical_cast

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

您可以使用C ++标准库文件中的一个stringstream:

 stringstream ss(str); int x; ss >> x; if(ss) { // <-- error handling // use x } else { // not a number } 

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

在C ++中查看error handling和stream的陷阱。

你可以使用stringstream的

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

C ++string工具包库(StrTk)具有以下解决scheme:

 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)
 {
   如果(0 == std :: distance(begin,end))
      返回false;
    v = 0;
    InputIterator it = begin;
    bool negative = false;
   如果('+'== * it)
       ++它;
   否则如果(' - '== *它)
    {
       ++它;
       negative = true;
    }
   如果(结束==它)
      返回false;
    while(end!= it)
    {
       const T digit = static_cast <T>(digit_table [static_cast <unsigned int>(* it ++)]);
      如果(0xFF ==数字)
         返回false;
       v =(10 * v)+数字;
    }
   如果(消极)
       v * = -1;
   返回true;
 }

InputIterator可以是无符号的char *,char *或std :: string迭代器,T可以是有符号的整型,如signed int,int或long

我觉得这三个环节总结起来:

stringstream和lexical_cast解决scheme大致相同,词法转换使用stringstream。

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

人们可以根据自己的需要来专注于lexical_cast,并使其更快。 这将是满足各方的最终解决scheme,干净而简单。

已经提到的文章显示了转换整数< – >string的不同方法之间的比较。 以下的方法是有道理的:旧的c-way,spirit.karma,fastformat,简单朴素的循环。

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

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

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

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

从上面提到的基本的简单循环是将string转换为int的最快方法,显然不是最安全的方法,strtol()看起来像一个更安全的解决scheme

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

如果你有C ++ 11,那么现在适当的解决scheme就是使用<string>中的C ++整数转换函数: stoistolstoulstollstoull 。 在给出不正确的input时,他们会抛出适当的例外,并在引擎盖下使用快速和小型strto*函数。

如果您坚持使用C ++的早期版本,那么您可以向前移植,以便在您的实现中模仿这些function。

我喜欢Dan Molding的回答 ,我只是添加了一点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; } 

它通过隐式转换对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它可以在C ++ 11之前工作。 也不像std::stoiboost::lexical_caststringstream它会抛出像“123hohoho”这样的奇怪string的exception。

NB:这个函数容忍前导空格,但不能结尾空格,即to_int(" 123")返回123,而to_int("123 ")抛出exception。 确保您的用例可以接受,或者调整代码。

这样的function可能是STL的一部分…

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

在有效的string之后添加了一个空格检查…这三行

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

增加了对parsing错误的检查。

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

这是完整的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; } 

我知道将String转换为int的三种方法:

或者使用stoi(String to int)函数或者直接使用Stringstream,第三种方式进行单独转换,代码如下:

第一种方法

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

第二种方法

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

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

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

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

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

如果你要从string转换为整数,你只需要执行以下操作。

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

输出将是102。

我知道这是一个较老的问题,但我碰到过很多次,到目前为止,还没有find一个很好的模板化解决scheme,具有以下特点:

  • 可以转换任何基地(和检测基地types)
  • 将检测错误的数据(即确保整个string,更less的前导/尾随空格,由转换消耗)
  • 将确保无论转换为何种types,string值的范围都是可以接受的。

所以,这是我的,带有testing表带。 因为它使用引擎盖下的C函数strtoull / strtoll,它总是首先转换为可用的最大types。 那么,如果你没有使用最大的types,它将执行额外的范围检查,以确认你的types没有超过(不足)stream动。 对于这一点,如果正确select了strtol / strtoul,性能会有所降低。 然而,它也适用于短裤/字符,据我所知,也没有标准的库函数。

请享用; 希望有人认为它有用。

 #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是用户登陆方法; it is overloaded so it can be called either like this:

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

或这个:

 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; a = StringToDecimal("100"); 

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

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 (eg negative input is disallowed if unsigned and it ensures the entire string was converted).

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.

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!

In C, you can use 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.