首页 文章

如何将字符串解析为C中的int?

提问于
浏览
251

将字符串(以char *表示)解析为int的C方式是什么?强大而清晰的错误处理是一个加号(而不是returning zero) .

17 回答

  • 66

    在新的C 11中有它的功能:stoi,stol,stoll,stoul等 .

    int myNr = std::stoi(myString);
    

    它会在转换错误上引发异常 .

    即使这些新函数仍然具有Dan所指出的 same issue :他们很乐意将字符串"11x"转换为整数"11" .

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

  • 22

    什么不该做

    这是我的第一条建议: do not use stringstream for this . 虽然起初它看起来很简单,但如果你想要健壮性和良好的错误处理,你会发现你必须做很多额外的工作 .

    这是一种直观地看起来应该工作的方法:

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

    这有一个主要问题: str2int(i, "1337h4x0r") 将高兴地返回 true 并且 i 将获得值 1337 . 我们可以通过确保转换后 stringstream 中没有其他字符来解决此问题:

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

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

    如果字符串中的数字不是10?我们可以尝试通过在尝试转换之前将流设置为正确的模式(例如 ss << std::hex )来容纳其他基础 . 但这意味着呼叫者必须先知道该号码的基础 - 呼叫者怎么可能知道呢?来电者甚至不知道这是一个号码!他们怎么能知道它是什么基础?我们可以强制要求输入到我们程序的所有数字都必须是10,并拒绝十六进制或八进制输入为无效 . 但这不是非常灵活或强大 . 这个问题没有简单的解决方案 . 你不能简单地为每个基数尝试一次转换,因为对于八进制数字(前导零),十进制转换将始终成功,并且对于某些十进制数字,八进制转换可能会成功 . 所以现在你必须检查一个前导零 . 可是等等!十六进制数也可以从前导零开始(0x ...) . 叹 .

    即使您成功处理上述问题,仍然存在另一个更大的问题:如果调用者需要区分错误输入(例如"123foo")和超出 int 范围的数字(例如"4000000000"用于32位) int )?使用 stringstream ,无法进行此区分 . 我们只知道转换是成功还是失败 . 如果失败了,我们无法知道失败的原因 . 正如您所看到的,如果您需要健壮性和清晰的错误处理, stringstream 还有很多不足之处 .

    这引出了我的第二条建议: do no use Boost's lexical_cast for this . 考虑一下 lexical_cast 文档的含义:

    如果需要对转换进行更高程度的控制,std :: stringstream和std :: wstringstream会提供更合适的路径 . 在需要非基于流的转换的情况下,lexical_cast是工作的错误工具,并非针对此类方案的特殊情况 .

    什么??我们已经看到 stringstream 的控制水平很差,但如果你需要"a higher level of control",它应该使用 stringstream 而不是 lexical_cast . 此外,因为 lexical_cast 只是 stringstream 的包装器,它会遇到与 stringstream 相同的问题:对多个数字基础的支持不足以及错误处理不佳 .

    最好的解决方案

    幸运的是,有人已经解决了上述所有问题 . C标准库包含 strtol 和系列,它们都没有这些问题 .

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

    对于处理所有错误情况并且还支持从2到36的任何数字基数的东西非常简单 . 如果 base 为零(默认值),它将尝试从任何基数转换 . 或者调用者可以提供第三个参数,并指定只应针对特定的基础进行转换 . 它功能强大,只需极少的工作量即可处理所有错误 .

    更喜欢 strtol (和家庭)的其他理由:

    • 它表现得更好runtime performance

    • 它引入了较少的编译时开销(其他人从头文件中获取了近20倍的SLOC)

    • 它产生最小的代码大小

    绝对没有充分的理由使用任何其他方法 .

  • 1

    这是比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
    }
    

    编辑:修复了字符串流版本,以便处理错误 . (感谢CMS和jk对原帖的评论)

  • 6

    您可以在更通用的界面中使用Boost's lexical_cast,其中wraps this . lexical_cast<Target>(Source) 失败后抛出 bad_lexical_cast .

  • 4

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

  • 153

    您可以使用C标准库中的字符串流:

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

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

    有关错误处理和C中的流的缺陷,请参阅Stream pitfalls .

  • 7

    你可以使用stringstream's

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

    我认为这三个链接总结了:

    stringstream和lexical_cast解决方案与使用stringstream的lexical cast大致相同 .

    词法演员的一些专业使用不同的方法请参阅 http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp 了解详情 . 整数和浮点数现在专门用于整数到字符串的转换 .

    人们可以根据自己的需要专门研究lexical_cast并快速完成 . 这将是满足各方的终极解决方案,简洁明了 .

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

    Lexical_cast在某些情况下是可以的,例如用于int到字符串的转换 .

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

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

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

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

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

    C++ String Toolkit Library (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;
    }
    

    InputIterator可以是unsigned char *,char *或std :: string迭代器,T应该是signed int,例如signed int,int或long

  • 7

    如果你有C 11,那么现在适当的解决方案是 <string>stoistolstoulstollstoull 中的C整数转换函数 . 如果给出错误的输入,它们会抛出适当的异常,并使用引擎盖下的快速和小型 strto* 功能 .

    如果您坚持使用早期版本的C,那么在您的实现中模仿这些函数将是前瞻性的 .

  • 21

    从C17开始,您可以使用 <charconv> Headers 中的 std::from_chars ,如here所述 .

    例如:

    #include <iostream>
    #include <charconv>
    #include <array>
    
    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;
        }
    }
    

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

  • 0

    我喜欢Dan Moulding's answer,我只是添加一些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" .

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

    这样的功能可能是STL的一部分......

  • 2

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

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

    1st Method

    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

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

    我喜欢Dan's answer,因为避免例外 . 对于嵌入式系统开发和其他低级系统开发,可能没有适当的异常框架可用 .

    在有效字符串后添加了对空格的检查......这三行

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

    添加了对解析错误的检查 .

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

    这是完整的功能..

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

    您可以使用此定义的方法 .

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

    如果您要从String转换为Integer,您只需执行以下操作即可 .

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

    输出将是102 .

  • 2

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

    • 可以转换任何base(和检测基类型)

    • 将检测错误数据(即确保转换消耗整个字符串,较少的前导/尾随空格)

    • 将确保无论转换为何种类型,字符串值的范围都是可接受的 .

    所以,这是我的,带有测试带 . 因为它在引擎盖下使用了C函数strtoull / strtoll,所以它总是首先转换为可用的最大类型 . 然后,如果您没有使用最大类型,它将执行额外的范围检查以验证您的类型未超过(下)流动 . 对于这个,它比正确选择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 是用户 - 土地方法;它被重载所以可以这样调用:

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

    或这个:

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

    我讨厌重复int类型,所以更喜欢后者 . 这确保了如果'a'的类型改变,则不会得到不好的结果 . 我希望编译器可以解决它:

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

    ...但是,C不会推断模板返回类型,所以这是我能得到的最好的 .

    实现非常简单:

    CstrtoxllWrapper 包装 strtoullstrtoll ,根据模板类型的signed-ness调用任何必要的并提供一些额外的保证(例如,如果无符号则不允许负输入,并确保整个字符串被转换) .

    CstrtoxllWrapperStringToSignedStringToUnsigned 使用,编译器可以使用最大类型(long long / unsigned long long);这允许执行最大转换 . 然后,如果有必要, StringToSigned / StringToUnsigned 对基础类型执行最终范围检查 . 最后, endpoints 方法 StringToDecimal 根据基础类型的signed-ness决定调用哪个StringTo *模板方法 .

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

  • 194

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

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

相关问题