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

2110
如何在C++中将int转换为等效的string?我知道两种方法。还有其他方法吗?
(1)
int a = 10;
char *intStr = itoa(a);
string str = string(intStr);

(2)

int a = 10;
stringstream ss;
ss << a;
string str = ss.str();

6
我认为你给出的两种方法都是不错的解决方案。这取决于需要使用方法的上下文。例如,如果您已经在使用流(stream),比如读写文件,则第二种方法是最好的选择。如果您需要将整数转换为字符串以传递给函数参数,则 itoa 可能是一种简单的方式。但是大多数情况下,整数到字符串的转换发生在处理文件时,所以流是合适的。 - Charles Brunet
63
选项1对你来说怎么可能有效?据我了解,itoa()需要三个参数。 - arkon
3
itoa 方法比流等效方法更快。此外,使用 itoa 方法可以重复使用字符串缓冲区(如果您经常生成字符串,则避免堆分配。例如,对于某些快速更新的数值输出)。或者,您可以生成自定义 streambuf 以减少一些分配开销等。首先构建流也不是低成本的事情。 - Pete
7
@Pete: 一旦你开始担心哪一个更快,你可以看一下这个网址:https://dev59.com/TG855IYBdhLWcg3wbjrO - Ben Voigt
25
请注意,itoa()不是标准的一部分,因此使用它会使您的代码不可移植,因为并非所有编译器都支持它。对于Linux而言,除非您使用的不是GCC,否则您肯定不行,因为GCC不支持此函数。如果您有C++0x,请按@Matthieu在他的答案中建议的去做。如果不是这种情况,请使用stringstream,因为它是一个得到广泛支持的特性,您的代码应该与每个C++编译器兼容。作为替代方案,您总是可以使用sprintf()。 - rbaleksandar
显示剩余2条评论
32个回答

2764

C++11 引入了std::stoi(以及每种数字类型的变体)和std::to_string,它们是 C 语言中 atoiitoa 的对应物,但是使用了 std::string

#include <string> 

std::string s = std::to_string(42);
所以这是我能想到的最短的方法。您甚至可以省略类型名称,使用auto关键字:
auto s = std::to_string(42);

注意:请参见 [string.conversions]21.5n3242 中)


220
to_string不是std的成员。解决方法:https://dev59.com/smcs5IYBdhLWcg3wSSDH - Ben
43
根据你所使用的编译器设置正确的语言标准,例如:g++ -std=c++11 someFile.cc - Thomas M. DuBuisson
15
@Steve:根据我了解,它应该是的。除了某个编译器外,在我所知道的所有编译器中,它都是std的一个成员。 - Mooing Duck
6
@Matthiew M. 我正在使用你建议的相同方法,但是我遇到了这个错误:错误:没有匹配参数列表的重载函数“std :: to_string”实例。我正在使用VS2010 c ++。 - user2643530
24
在VS2010中,您需要将转换整数显式强制转换为以下类型之一[_Longlong、_ULonglong、long double];例如:string s = to_string((_ULonglong)i); - Zac
显示剩余17条评论

232

C++20: 现在的惯用方式是使用std::format


C++17:

几年后与 @v.oddou 讨论后,C++17 提供了一种无需使用宏丑陋代码的途径来实现最初基于宏的类型不可知解决方案(下面保留)。

// variadic template
template < typename... Args >
std::string sstr( Args &&... args )
{
    std::ostringstream sstr;
    // fold expression
    ( sstr << std::dec << ... << args );
    return sstr.str();
}

用法:

int i = 42;
std::string s = sstr( "i is: ", i );
puts( sstr( i ).c_str() );

Foo x( 42 );
throw std::runtime_error( sstr( "Foo is '", x, "', i is ", i ) );

C++98:

由于“将...转换为字符串”是一个经常出现的问题,我总是在我的C++源代码的中央头文件中定义SSTR()宏:

#include <sstream>

#define SSTR( x ) static_cast< std::ostringstream & >( \
        ( std::ostringstream() << std::dec << x ) ).str()

使用非常简单:

int i = 42;
std::string s = SSTR( "i is: " << i );
puts( SSTR( i ).c_str() );

Foo x( 42 );
throw std::runtime_error( SSTR( "Foo is '" << x << "', i is " << i ) );

如果你不能使用 C++11 的 std::to_string 和 Boost 的 lexical_cast<>,上面的代码是兼容 C++98 的,并且不需要任何第三方库,但这两个其他解决方案的性能要更好。


2
我对dynamic_cast不是很熟悉,但我正在使用clang进行编译,所以它抱怨了它。如果我只省略dynamic_cast,那么它就可以正常编译;在这种情况下,dynamic_cast起到什么作用?我们已经创建了一个ostringstream,那么为什么还要进行转换? - Mathew
2
@Mathew:我的回答中的链接提供了构造的每个部分的详细描述。虽然我们创建了一个ostringstream,但我们在其上调用了operator<<(),它返回ostream&--对于该类型,.str()未定义。我真的很想知道clang如何在没有强制转换的情况下使其工作(或者为什么它使用强制转换会生成错误)。这个结构在许多地方都有发布,并且我已经在许多不同的编译器上使用它十多年,包括MSVC,GCC和XLC,所以我对clang的反应感到非常惊讶。 - DevSolar
9
我只是出于好奇来参加聚会,然后投了反对票。原因是:太多人支持一个不够优雅且可能很慢的解决方案。1. 使用宏。我并不是反对所有宏,但这个宏太短了,而且最终客户总是担心参数重复,以及未经保护的多行宏。(没有使用do {} while(0)保护)。2. dynamic_cast。除非你想断言库确实是按照你所希望的方式实现的,否则似乎只需要使用static_cast。在这种情况下,你应该使用boost::polymorphic_downcast。 - v.oddou
2
@v.oddou:当然你可以批评。但是第一点是无效的——宏是一个单独的语句,do { } while( 0 ) 不会添加任何内容。对于第二点和第三点,你可能有一些观点——这可以通过静态转换来完成,也许你们其中的一位模板专家可以想出一个更好的接口。但是正如我所说,这绝不是我的发明。看看周围,这个宏(宏!)相当普遍。这本身就是 POLA 的一个例子。我可能会稍微玩弄一下,使其更加“简洁”。 - DevSolar
1
@v.oddou:看看我在C++17中发现的东西。 :-) 我希望你喜欢这个更新的答案。 - DevSolar
显示剩余5条评论

146

当前的 C++

C++11 开始,有一个为整数类型重载的 std::to_string 函数,因此您可以使用以下代码:

int a = 20;
std::string s = std::to_string(a);
// or: auto s = std::to_string(a);

这个标准定义了这些操作等价于使用sprintf进行转换(使用与所提供对象类型相匹配的转换说明符,例如%d表示int),将其转换为足够大的缓冲区,然后创建包含该缓冲区内容的std::string

旧版 C++

对于旧版(C++11之前)编译器,可能最常见的简单方法就是将基本上你的第二选择封装成一个通常称为lexical_cast的模板,例如在Boost中的那个,因此您的代码看起来像这样:

int a = 10;
string s = lexical_cast<string>(a);

其中一个好处是它还支持其他类型的强制转换(例如,相反方向同样有效)。

同时需要注意的是,虽然 Boost 的 lexical_cast 最初只是写入一个 stringstream 中,然后从流中提取回来,但现在已经添加了一些功能。首先,已经为许多常见类型添加了特化,因此对于许多常见类型来说,它比使用 stringstream 更快。其次,它现在会检查结果,因此(例如)如果将字符串转换为 int,如果字符串包含无法转换为 int 的内容(例如 123abc),它可能会抛出异常。


2
不错,我更喜欢Kevin的答案,因为他展示了include和namespace。只是有点小问题。:) 不过做得很好! - Jason R. Mick
5
如果你没有C++11支持的话,我认为这是可行的方式。 - Alexander Oh

122
我通常使用以下方法:

我通常使用以下方法:

#include <sstream>

template <typename T>
  std::string NumberToString ( T Number )
  {
     std::ostringstream ss;
     ss << Number;
     return ss.str();
  }

这里详细描述了相关信息


18
@lifebalance:我从未见过这样的行为。 - Rasoul
26
你不需要对新创建的ostringstream对象进行clear()操作。clear()函数会重置错误/文件结束标志位,但由于还没有发生任何错误/文件结束的情况,因此没有必要进行该操作。 - Remy Lebeau
2
@Rasoul NumberToString(23213.123) 产生的结果是 23213.1,而 std::to_string(23213.123) 产生的结果是 23213.123000。这是怎么回事? - Killzone Kid
1
我通常不会为三行代码编写一个函数,但这样可以通过函数名使其自我记录。清晰度与自我记录——它们不应该冲突,对吧? - phorgan1
1
使用.flags(...)读取和清除格式标志,使用.str("")清除现有字符串。 - xryl669
显示剩余2条评论

58

您可以使用C++11中提供的std::to_string,如Matthieu M.所建议的那样:

std::string s = std::to_string(42);

或者,如果性能很重要(例如,如果您执行大量转换),则可以使用{fmt}库中的fmt::format_int将整数转换为std::string

std::string s = fmt::format_int(42).str();

或者一个 C 字符串:

fmt::format_int f(42);
const char* s = f.c_str();

后者不执行任何动态内存分配,并且在 Boost Karma 基准测试中比 libstdc++ 实现的 std::to_string 快70%以上。有关详细信息,请参见每秒将一亿个整数转换为字符串

免责声明:我是 {fmt} 库的作者。


2
我对声称没有任何动态内存分配同时保持线程安全(可重入)的说法感到好奇,所以我阅读了你的代码——c_str()返回一个在fmt::FormatInt类内声明的缓冲区指针——因此返回的指针将在分号处无效——请参见https://dev59.com/FG855IYBdhLWcg3wp2R0。 - Soren
1
是的,与 std::string::c_str() 相同的行为(因此命名为这个)。如果你想在完整表达式之外使用它,请构造一个对象 FormatInt f(42); 然后你可以使用 f.c_str() 而不必担心它被销毁。 - vitaut
1
当我尝试使用std::to_string(num)将int转换为string时,我得到了一些奇怪的东西。如果我将结果存储在变量中,并尝试像stringNum[1]或stringNum[n]这样访问它,随着n的增加,我会得到垃圾值。 - user8951490
1
这绝对是这个问题的正确答案:高效且标准的非状态代码。 - xryl669

45

如果您已经安装了Boost(应该安装):

#include <boost/lexical_cast.hpp>

int num = 4;
std::string str = boost::lexical_cast<std::string>(num);

5
同意安装 Boost。我认为很多情况下需要格式化字符串。为此,我更喜欢使用 boost::format,例如 format("%02d", number ).str()。 - Werner Erasmus

39

使用stringstream会更容易:

#include <sstream>

int x = 42;          // The integer
string str;          // The string
ostringstream temp;  // 'temp' as in temporary
temp << x;
str = temp.str();    // str is 'temp' as string

或者创建一个函数:

#include <sstream>

string IntToString(int a)
{
    ostringstream temp;
    temp << a;
    return temp.str();
}

19

就我所知,在纯C++中没有这样的功能。但是可以对你提到的东西进行一点修改。

string s = string(itoa(a));

应该可以工作,而且代码很短。


69
itoa() 不是标准函数! - cartoonist
4
@cartoonist: 那这是什么意思? - user541686
25
这个函数在 ANSI-C 和 C++ 中没有定义,因此不被一些编译器支持,例如 g++。 - cartoonist
1
可移植性 此函数未在 ANSI-C 中定义,也不是 C++ 的一部分,但某些编译器支持它。https://gcc.godbolt.org/z/MaEEf4evW - kfsone
@cartoonist 我认为这与g++无关,而是与glibc有关。我希望他们能将所有这些函数添加到glibc中。这会让人们的工作更加容易。 - Isaac D. Cohen

18

sprintf()函数非常适合进行格式转换。然后,您可以像在 1. 中那样将生成的 C 字符串赋值给 C++ 字符串。


2
呵呵,是的。但是,当处理C字符串时,我通常依赖于snprintf()和相关函数来处理任何重要的内容。 - Throwback1986
1
@MatthieuM。你的评论进一步证明,你并不是这样的人。如果输出由于限制而被截断,则返回值是字符数(不包括终止的空字节),如果有足够的空间,则会写入最终字符串。因此,大小或更大的返回值意味着输出已被截断。因此,您可以使用“NULL”和零大小调用它以获取所需的缓冲区大小。 - user1095108
2
@user1095108:我认为你弄错了 snprintf(注意 SNP 前缀)和 sprintf(注意 SP 前缀)。你向前者传递大小,它会避免溢出,然而后者不知道缓冲区的大小,因此可能会溢出。 - Matthieu M.
1
这个想法是先调用 snprintf 变体,然后再调用 sprintf 变体。由于此时缓冲区大小已知,因此调用 sprintf 变得完全安全。 - user1095108
1
@user1095108 啊,是的。如果第一个缓冲区 to snprintf 不够大,返回值会告诉您需要多大的缓冲区。我仍然会在第二次调用时使用snprintf,因为依赖于sprintf和snprintf的实现匹配是不必要危险的。 - mabraham
显示剩余4条评论

15

使用stringstream进行数字转换是危险的!

请看std::ostream::operator<<,其中说明了operator<<插入已格式化的输出。

根据当前的区域设置,大于三位数的整数可能会转换为四位数的字符串,并添加额外的千位分隔符。

例如,int = 1000 可能会被转换为一个字符串 1.001。这可能导致比较操作完全无效。

因此,我强烈建议使用std::to_string方法。它更容易使用,并且达到预期的效果。

来自std::to_string:

C++17 提供了 std::to_chars 作为一种更高效、独立于区域设置的替代方法。


2
我同意,如果你需要交换数据,这是一个严重的问题。不幸的是,std::to_string也使用当前语言环境(请参见http://en.cppreference.com/w/cpp/string/basic_string/to_string,“注”部分)。几乎所有标准工具(从字符串流到`sprintf`,但也包括`sscanf`等)都使用当前语言环境。直到最近,当它严重影响我时,我才意识到这一点。目前使用自己编写的东西,不难制作。 - Bert Bril
在上面的链接中还指出,C++17提供了std::to_chars作为一种更高性能的区域设置无关的替代方案。 - YesThatIsMyName
不幸的是,我在未来的一年或几年中将被困在C++11中(幸运的是,这已经是相当大的改进了)。 - Bert Bril
1
from_chars和to_chars本来是完美的,但很遗憾它们没有提供wchar_t变量。 - gast128

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