【问题标题】:How to parse a string to an int in C++?如何在 C++ 中将字符串解析为 int?
【发布时间】:2010-09-16 16:56:19
【问题描述】:

将字符串(以 char * 形式给出)解析为 int 的 C++ 方法是什么?强大而清晰的错误处理是一个优点(而不是returning zero)。

【问题讨论】:

标签: c++ parsing


【解决方案1】:

从 C++17 开始,您可以使用 <charconv> 标头中的 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;
    }
}

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

【讨论】:

    【解决方案2】:

    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 应为带符号的 int,例如带符号的 int、int 或 long

    【讨论】:

    • 警告这个实现看起来不错,但据我所知没有处理溢出。
    • 代码不处理溢出。 v = (10 * v) + digit; 在文本值为INT_MIN 的字符串输入中不必要地溢出。与简单的 digit &gt;= '0' &amp;&amp; digit &lt;= '9' 相比,表的价值值得怀疑
    【解决方案3】:

    我知道三种将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 ;
    
    }
    

    【讨论】:

      【解决方案4】:

      我喜欢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;
      }
      

      【讨论】:

      • @chux 添加了代码来解决您提到的问题。
      • 1) 仍然无法检测到像" " 这样的输入错误。未指定strtol() 以在未发生转换时设置errno。最好使用if (s == end) return INCONVERTIBLE; 来检测没有转换。然后if (*s == '\0' || *end != '\0') 可以简化为if (*end) 2) || l &gt; LONG_MAX|| l &lt; LONG_MIN 毫无用处——它们永远不会是真的。
      • @chux 在 mac 上,设置了 errno 用于解析错误,但在 linux 上没有设置 errno。更改代码以依赖“结束”指针来检测。
      【解决方案5】:

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

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

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

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

      #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,根据模板类型的有符号性调用任何必要的,并提供一些额外的保证(例如,如果无符号则不允许负输入,并确保整个字符串被转换) .

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

      我认为大部分垃圾都可以被编译器优化掉;几乎所有东西都应该是编译时确定的。任何关于这方面的评论都会让我感兴趣!

      【讨论】:

      • "使用最大类型"--> 为什么是long long 而不是intmax_t
      • 自信你想要if (ePtr != str)。此外,使用isspace((unsigned char) *ePtr) 正确处理*ePtr 的负值。
      【解决方案6】:

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

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

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

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

      输出将是 102。

      【讨论】:

      • 身份证。鉴于已接受的std::stoi() 等其他答案,围绕atoi 编写定义宏似乎不像“C++ 方式”。
      • 我发现使用预定义的方法更有趣:P
      【解决方案7】:

      我喜欢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 的一部分...

      【讨论】:

        【解决方案8】:

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

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

        带有标准库stringstream的C++:(感谢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 对原帖的评论)

        【讨论】:

        • 请更新您的 stringstream 版本以包括对 stringstream::fail() 的检查(根据提问者“稳健和清晰的错误处理”的要求)
        • 您的 stringstream 版本将接受诸如“10haha”之类的内容而不会抱怨
        • 如果你把它从 ((ss >> num).fail()) 改为 (!(ss >> num).fail() && (ss >> ws).eof())想要像 lexical_cast 一样的处理
        • 带有标准库 stringstream 方法的 C++ 不适用于诸如“12-SomeString”之类的字符串,即使使用 .fail() 检查。
        • C++11 现在包括标准的快速函数
        【解决方案9】:

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

        解析 C 字符串 str,将其内容解释为整数,返回为 int 类型的值。

        【讨论】:

        • 当我在问题中链接到atoi 时,我知道这一点。问题显然不是关于 C,而是关于 C++。 -1
        【解决方案10】:

        如果你有 C++11,现在合适的解决方案是 &lt;string&gt; 中的 C++ 整数转换函数:stoistolstoulstollstoull。当输入不正确时,它们会抛出适当的异常,并在后台使用快速而小型的 strto* 函数。

        如果您坚持使用 C++ 的早期版本,您可以在实现中模仿这些功能。

        【讨论】:

          【解决方案11】:

          在新的 C++11 中,有相应的函数:stoi、stol、stoll、stoul 等等。

          int myNr = std::stoi(myString);
          

          它会在转换错误时抛出异常。

          即使这些新函数仍然存在同样的问题,正如 Dan 指出的那样:它们很乐意将字符串“11x”转换为整数“11”。

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

          【讨论】:

          • 但他们接受的参数不止于此,其中一个是指向 size_t 的点,如果不是 null,则设置为第一个未转换的字符
          • 是的,使用 std::stoi 的第二个参数可以检测到无效输入。不过,您仍然必须推出自己的转换功能......
          • 就像接受的答案一样,但有了这些标准功能,imo 会更干净
          • 请记住,这些函数中的第二个参数可用于判断整个字符串是否已转换。如果生成的size_t 不等于字符串的长度,那么它会提前停止。在这种情况下,它仍会返回 11,但 pos 将是 2 而不是字符串长度 3。coliru.stacked-crooked.com/a/cabe25d64d2ffa29
          【解决方案12】:

          我认为这三个链接总结起来:

          stringstream 和 lexical_cast 解决方案与使用 stringstream 的词法转换大致相同。

          词汇转换的一些特化使用不同的方法,请参阅 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;
          }
          

          【讨论】:

            【解决方案13】:

            什么不能做

            这是我的第一条建议:不要为此使用字符串流。虽然一开始使用起来可能看起来很简单,但您会发现如果您想要健壮性和良好的错误处理能力,就必须做很多额外的工作。

            这是一种直觉上看起来应该可行的方法:

            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 &lt;&lt; std::hex)来尝试适应其他碱基。但这意味着调用者必须先验知道数字是什么基数——调用者怎么可能知道呢?来电者还不知道号码是多少。他们甚至不知道它一个数字!怎么能指望他们知道它的基础是什么?我们可以只要求输入到我们程序的所有数字必须以 10 为底,并拒绝十六进制或八进制输入为无效。但这不是很灵活或健壮。这个问题没有简单的解决方案。您不能简单地为每个基数尝试一次转换,因为对于八进制数(带有前导零),十进制转换总是会成功,而对于某些十进​​制数,八进制转换可能会成功。所以现在你必须检查前导零。可是等等!十六进制数字也可以以前导零开头(0x ...)。叹息。

            即使你成功处理了上述问题,还有另一个更大的问题:如果调用者需要区分错误输入(例如“123foo”)和超出int 范围的数字怎么办? (例如“4000000000”代表 32 位 int)?对于stringstream,无法区分。我们只知道转换是成功还是失败。如果它失败了,我们就无法知道为什么它失败了。如您所见,如果您想要稳健性和清晰的错误处理,stringstream 有很多不足之处。

            这引出了我的第二条建议:不要为此使用 Boost 的 lexical_cast。想想lexical_cast 文档是怎么说的:

            控制程度更高的地方 需要过度转换, std::stringstream 和 std::wstringstream 提供更多 合适的路径。在哪里 非基于流的转换是 必需,lexical_cast 是错误的 工作的工具,而不是 此类场景的特殊情况。

            什么?我们已经看到stringstream 的控制水平很差,但它说如果你需要“更高水平的控制”,应该使用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
            • 它引入了更少的编译时开销(其他从头文件中提取的 SLOC 增加了近 20 倍)
            • 它会产生最小的代码大小

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

            【讨论】:

            • @JamesDunne:POSIX 要求 strtol 是线程安全的。 POSIX 还要求errno 使用线程本地存储。即使在非 POSIX 系统上,几乎所有多线程系统上的 errno 实现都使用线程本地存储。最新的 C++ 标准要求 errno 符合 POSIX。最新的 C 标准还要求 errno 具有线程本地存储。即使在绝对不兼容 POSIX 的 Windows 上,errno 也是线程安全的,并且通过扩展,strtol 也是如此。
            • 我不能真正理解你反对使用 boost::lexical_cast 的理由。正如他们所说,std::stringstream 确实提供了很多控制 - 从错误检查到自己确定基础,您都可以做所有事情。当前的文档是这样说的:“对于更多涉及的转换,例如精度或格式需要比 lexical_cast 的默认行为提供的更严格的控制,建议使用传统的 std::stringstream 方法。”
            • 这是 C++ 中不恰当的 C 编码。标准库为此包含std::stol,它将适当地抛出异常而不是返回常量。
            • @fuzzyTew 我在 std::stol 甚至被添加到 C++ 语言之前写了这个答案。也就是说,我认为说这是“C++ 中的 C 编码”并不公平。当std::strtol 是 C++ 语言的明确一部分时,说它是 C 编码是很愚蠢的。我的答案在编写时完美适用于 C++,即使使用新的std::stol,它仍然适用。调用可能抛出异常的函数并不总是最适合每种编程情况。
            • @fuzzyTew:磁盘空间不足是一种例外情况。计算机生成的格式错误的数据文件是一种例外情况。但用户输入中的拼写错误也不例外。最好有一种能够处理正常、非异常解析失败的解析方法。
            【解决方案14】:

            您可以在更通用的界面中使用Boost's lexical_cast,其中wraps thislexical_cast&lt;Target&gt;(Source) 在失败时抛出 bad_lexical_cast

            【讨论】:

            【解决方案15】:

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

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

            流状态将设置为失败 如果遇到非数字时 试图读取一个整数。

            请参阅Stream pitfalls,了解 C++ 中错误处理和流的缺陷。

            【讨论】:

            • C++ stringstream 方法对诸如“12-SomeString”之类的字符串不起作用,即使有“流状态”检查。
            【解决方案16】:

            您可以使用stringstream's

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

            【讨论】:

            • 但这不处理任何错误。您必须检查流是否失败。
            • 对,你必须检查流 if((ss >> num).fail()){ //ERROR }
            • C++ 字符串流方法对诸如“12-SomeString”之类的字符串不起作用,即使有“流状态”检查
            【解决方案17】:

            良好的“旧 C 方法”仍然有效。我推荐 strtol 或 strtoul。在返回状态和“endPtr”之间,您可以提供良好的诊断输出。它还可以很好地处理多个碱基。

            【讨论】:

            • 哦,请不要在编写 C++ 时使用这些旧的 C 东西。在 C++ 中有更好/更容易/更清洁/更现代/更安全的方法来做到这一点!
            • 当人们关心解决问题的“更现代”的方法时,这很有趣。
            • 我查看了其他答案,到目前为止,没有什么比这更好/更容易/更清洁或更安全了。海报说他有一个字符 *。这限制了您将获得的安全性:)
            • @JMiller 公平地说,在 C 和 C++ 的情况下,这是因为老式方法通常具有明显的缺陷,从笨拙到低效,再到完全不安全。在std::strtol 的情况下,除非您手动检查字符串是否解析为0,否则您无法知道您是否已成功解析0,或者该函数是否失败,并且在您完成时你不必要地重复工作。更现代的方法 (std::from_chars) 不仅会告诉您函数何时失败,还会告诉您失败的原因为什么,这有助于向最终用户提供反馈。
            • 13 年后我正在查找这个问题,只是为了找到我自己的答案。我同意@Pharap 的观点,2008 年还不存在的 std::from_chars 现在可能是 C++ 中更好的答案。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-22
            • 2016-07-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多