如何在C++中将数字转换为字符串,以及如何将字符串转换为数字

135

由于这个问题每周都会被问到,这个FAQ可能会帮助很多用户。

  • 如何在C++中将整数转换为字符串

  • 如何在C++中将字符串转换为整数

  • 如何在C++中将浮点数转换为字符串

  • 如何在C++中将字符串转换为浮点数


1
对于像这样的转换,我有一个书签 https://www.converttypes.com/ - Firzok Nadeem
6个回答

141

升级至C++11

C++11 标准开始,将字符串转为数字以及反之的转换已经内置于标准库中。下面所有的函数都包含在 <string> 中(参见第 21.5 段)。

字符串转数字

float              stof(const string& str, size_t *idx = 0);
double             stod(const string& str, size_t *idx = 0);
long double        stold(const string& str, size_t *idx = 0);
int                stoi(const string& str, size_t *idx = 0, int base = 10);
long               stol(const string& str, size_t *idx = 0, int base = 10);
unsigned long      stoul(const string& str, size_t *idx = 0, int base = 10);
long long          stoll(const string& str, size_t *idx = 0, int base = 10);
unsigned long long stoull(const string& str, size_t *idx = 0, int base = 10);

这些函数每个都以字符串作为输入,尝试将其转换为数字。如果无法构造有效数字,例如因为没有数字数据或该数字超出类型的范围,则会引发异常(std::invalid_argumentstd::out_of_range)。

如果转换成功且 idx 不为 0,那么 idx 将包含未用于解码的第一个字符的索引。这可能是最后一个字符后面的索引。

最后,整数类型允许指定基数,对于大于 9 的数字,假定使用字母表(a=10 直到 z=35)。您可以在此处了解有关可解析浮点数、有符号整数和无符号整数的确切格式的更多信息:浮点数有符号整数无符号整数

最后,对于每个函数,还有一种重载形式,它将接受 std::wstring 作为其第一个参数。

数字转字符串

string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

这些方法更为简单,只需要传入相应的数字类型,即可获得一个字符串。如果需要格式化选项,可以返回到C++03中的stringstream选项并使用流操作符来进行操作,这在另一个答案中已经解释了。

正如评论中所指出的,这些函数会回退到默认尾数精度,可能不是最大精度。如果您的应用程序需要更高的精度,则最好返回其他字符串格式化过程。

还有类似于to_wstring命名的函数,它们将返回一个std::wstring


4
std::to_string对浮点数类型失去了很多精度。例如,double f = 23.4323897462387526; std::string f_str = std::to_string(f); 返回的字符串是23.432390。这使得使用这些函数回合浮点值变得不可能。 - fun4jimmy
@fun4jimmy 这是标准还是实现特定的限制?我会把它加入到答案中。不过,将浮点数通过字符串进行往返转换并不是一个好主意。 - KillianDS
C++标准规定:“返回值:每个函数均返回一个字符串对象,其中包含其参数的字符表示形式,该表示形式将通过具有格式说明符为“%d”、“%u”、“%ld”、“%lu”、“%lld”、“%llu”、“%f”、“%f”或“%Lf”的sprintf(buf,fmt,val)调用生成,其中buf指定足够大的内部字符缓冲区。”我查看了C99标准的printf,并认为小数位数取决于float.h中的#define DECIMAL_DIG。 - fun4jimmy
Bruce Dawson在他的博客上有一些关于浮点数往返舍入所需精度的好文章。博客链接 - fun4jimmy
2
所有这些函数都受全局语言环境的影响,如果您使用库,尤其是使用线程,可能会导致问题。请参见我的问题:https://dev59.com/Uo7da4cB1Zd3GeqP7whU - Ident
显示剩余4条评论

89

如何在C++03中将数字转换为字符串

  1. 不要使用itoa或itof函数,因为它们是非标准的,因此不具备可移植性。
  2. 使用字符串流

 #include <sstream>  //include this to use string streams
 #include <string> 

int main()
{    
    int number = 1234;

    std::ostringstream ostr; //output string stream
    ostr << number; //use the string stream just like cout,
    //except the stream prints not to stdout but to a string.

    std::string theNumberString = ostr.str(); //the str() function of the stream 
    //returns the string.

    //now  theNumberString is "1234"  
}

请注意,您也可以使用字符串流将浮点数转换为字符串,并按您想要的方式格式化字符串,就像使用cout一样。

std::ostringstream ostr;
float f = 1.2;
int i = 3;
ostr << f << " + " i << " = " << f + i;   
std::string s = ostr.str();
//now s is "1.2 + 3 = 4.2" 

您可以使用流操作符,例如std::endlstd::hex和函数std::setw()std::setprecision()等,与字符串流的使用方式完全相同,就像使用cout一样。

不要混淆std::ostringstreamstd::ostrstream。后者已被弃用。

使用boost lexical cast进行转换。如果您对boost不熟悉,建议从像这个lexical_cast这样的小库开始。要下载和安装boost及其文档,请点击此处。尽管boost不是C++标准的一部分,但许多boost库最终都会被标准化,并且boost被广泛认为是最好的C++库之一。

Lexical cast基本上是在使用流,因此这个选项与前一个选项基本相同,只是更简洁。

#include <boost/lexical_cast.hpp>
#include <string>

int main()
{
   float f = 1.2;
   int i = 42;
   std::string sf = boost::lexical_cast<std::string>(f); //sf is "1.2"
   std::string si = boost::lexical_cast<std::string>(i); //sf is "42"
}

如何在 C++03 中将字符串转换为数字

  1. 最轻量级的选项是继承自 C 的函数 atoi (用于整数) 和 atof (用于浮点数)。这些函数以 C 风格的字符串作为参数(const char *),因此它们的使用可能被认为是不太好的 C++ 实践 。cplusplus.com 提供了易于理解的文档,包括有关 atoiatof 在输入错误的情况下的行为。但是该链接中存在一个错误,根据标准,如果输入的数字过大而无法适应目标类型,则其行为未定义。

#include <cstdlib> //the standard C library header
#include <string>
int main()
{
    std::string si = "12";
    std::string sf = "1.2";
    int i = atoi(si.c_str()); //the c_str() function "converts" 
    double f = atof(sf.c_str()); //std::string to const char*
}
使用字符串流(此次是输入字符串流istringstream)。同样,istringstream的使用方式类似于cin。不要将istringstream与已弃用的istrstream混淆。
#include <sstream>
#include <string>
int main()
{
   std::string inputString = "1234 12.3 44";
   std::istringstream istr(inputString);
   int i1, i2;
   float f;
   istr >> i1 >> f >> i2;
   //i1 is 1234, f is 12.3, i2 is 44  
}
  • 使用boost lexical cast

  • #include <boost/lexical_cast.hpp>
    #include <string>
    
    int main()
    {
       std::string sf = "42.2"; 
       std::string si = "42";
       float f = boost::lexical_cast<float>(sf); //f is 42.2
       int i = boost::lexical_cast<int>(si);  //i is 42
    }       
    

    如果输入不正确,lexical_cast会抛出类型为boost::bad_lexical_cast的异常。


    4
    atoi 的cplusplus文档并不好,它是错误的。它没有提到如果字符串的数值不能在 int 中表示,则行为未定义。相反,它说超出范围的值被夹紧到 INT_MAX / INT_MIN,但我在 C++03 或 C89 中找不到这样的规定。对于不受信任/未经验证的输入或处理流不支持的基数时,您需要使用 strtol,它具有定义的错误行为。对于 atof / strtod,类似的评论适用。 - Steve Jessop
    2
    cplusplus.com关于"atoi"的描述是错误的。它说返回值为“如果无法进行有效转换,则返回零值。如果正确的值超出可表示值的范围,则返回INT_MAX或INT_MIN。”,但规范指出:“如果结果的值无法表示,则行为未定义。”并且“除了错误行为外,它们等同于(int)strtol(nptr,(char **)NULL,10)。atoi[...]函数返回转换后的值。” cplusplus.com被认为是初学者信息来源极差的网站。 - Johannes Schaub - litb
    istr >> i1 >> f >> i2; 这里严重缺少了一个成功检查。 - sbi
    4
    可能需要添加 std::to_string - Pubby
    1
    @ArmenTsirunyan:我这边给你加了10分,这是我见过的最好的答案之一,而且非常深入。感谢你的回答。 - Abhineet
    显示剩余2条评论

    4
    在C++17中,头文件charconv中引入了新函数std::to_charsstd::from_charsstd::to_chars是与语言环境无关、不分配内存且不抛出异常的。提供了其他库(如std::sprintf)使用的格式化策略的一个小子集。
    std::to_chars开始,std::from_chars同样如此。
    仅当两个函数来自相同实现时,才提供std::to_chars格式化的每个浮点值都可以由std::from_chars精确恢复的保证。
     // See en.cppreference.com for more information, including format control.
    #include <cstdio>
    #include <cstddef>
    #include <cstdlib>
    #include <cassert>
    #include <charconv>
    
    using Type =  /* Any fundamental type */ ;
    std::size_t buffer_size = /* ... */ ;
    
    [[noreturn]] void report_and_exit(int ret, const char *output) noexcept 
    {
        std::printf("%s\n", output);
        std::exit(ret);
    }
    void check(const std::errc &ec) noexcept
    {
        if (ec ==  std::errc::value_too_large)
            report_and_exit(1, "Failed");
    }
    int main() {
        char buffer[buffer_size];        
        Type val_to_be_converted, result_of_converted_back;
    
        auto result1 = std::to_chars(buffer, buffer + buffer_size,  val_to_be_converted);
        check(result1.ec);
        *result1.ptr = '\0';
    
        auto result2 = std::from_chars(buffer, result1.ptr, result_of_converted_back);
        check(result2.ec);
    
        assert(val_to_be_converted == result_of_converted_back);
        report_and_exit(0, buffer);
    }
    

    虽然编译器目前尚未完全实现,但它肯定会被实现。


    0

    我从StackOverflow的某个地方偷了这个方便的类来将任何可流式的内容转换为字符串:

    // make_string
    class make_string {
    public:
      template <typename T>
      make_string& operator<<( T const & val ) {
        buffer_ << val;
        return *this;
      }
      operator std::string() const {
        return buffer_.str();
      }
    private:
      std::ostringstream buffer_;
    };
    

    然后你可以这样使用它;

    string str = make_string() << 6 << 8 << "hello";
    

    非常巧妙!

    我也使用这个函数将字符串转换为任何可流式化的内容,尽管如果您尝试解析不包含数字的字符串,则它并不是非常安全;(而且它也不像上一个那么聪明)

    // parse_string
    template <typename RETURN_TYPE, typename STRING_TYPE>
    RETURN_TYPE parse_string(const STRING_TYPE& str) {
      std::stringstream buf;
      buf << str;
      RETURN_TYPE val;
      buf >> val;
      return val;
    }
    

    使用方法:

    int x = parse_string<int>("78");
    

    你可能也需要wstrings的版本。


    6
    这正是boost::lexical_cast所做的。而且boost以更通用的方式来实现它。 - Armen Tsirunyan
    没错,我浏览了第一个答案,但没有看到boost :: lexical_casts。 - Viktor Sehr

    0
    如果你仍然将其设计为一个独立的函数,编写天真而通用的代码会更容易。
    string IntToStr(int n)
    {
      string res;
      bool s=n<0;
      n=abs(n);
      do
        res=char(n%10+48)+res;
      while(n/=10);
      return s ? "-"+res : res;
    }
    

    -2
    #include <iostream>
    #include <string.h>
    using namespace std;
    int main() {
       string s="000101";
       cout<<s<<"\n";
       int a = stoi(s);
       cout<<a<<"\n";
       s=to_string(a);
       s+='1';
       cout<<s;
       return 0;
    }
    

    产生输出:
    • 000101
    • 101
    • 1011

    1
    你好,答案不完整(没有浮点转换),而且是错误的(to_string未定义)。请确保提供有用的答案。 - Massimo Costa

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接