如何在一行上连接多个C++字符串?

191

C#有一个语法特性,允许您在一行上将多个数据类型连接在一起。

string s = new String();
s += "Hello world, " + myInt + niceToSeeYouString;
s += someChar1 + interestingDecimal + someChar2;

在C++中该怎么做呢?据我所见,你需要将它们分别放在不同的行上完成操作,因为它不支持使用+运算符连接多个字符串/变量。这样做是可以的,但看起来不太整洁。

string s;
s += "Hello world, " + "nice to see you, " + "or not.";

以上代码会产生一个错误。


7
如前所述,这并不是因为“它不支持使用+运算符添加多个字符串/变量”,而是因为您正在尝试将char *指针相加。这就是导致错误的原因 - 因为对指针求和是没有意义的。如下所述,将至少第一个操作数转换为std::string,就不会出现错误了。 - underscore_d
1
产生了哪个错误? - Wolf
可能是如何连接std :: string和int?的重复问题。 - vitaut
25个回答

290
#include <sstream>
#include <string>

std::stringstream ss;
ss << "Hello, world, " << myInt << niceToSeeYouString;
std::string s = ss.str();

看看这篇由Herb Sutter撰写的Guru Of The Week文章:Manor Farm的字符串格式化程序


7
请尝试以下代码:std::string s = static_cast<std::ostringstream&>(std::ostringstream().seekp(0) << "HelloWorld" << myInt << niceToSeeYouString).str(); 该代码可以将字符串、整数和其他字符串类型的变量组合成一个字符串,并将其存储在s中。 - Byzantian
57
ss << "哇,C++中的字符串拼接很令人印象深刻" << "或者也许不是。" - joaerl
5
还有一种方法可以实现:使用多个追加操作: string s = string("abc").append("def").append(otherStrVar).append(to_string(123)); - Patricio Rossi
1
std::stringstream ss; ss << "Hello, world, " << myInt << niceToSeeYouString; std::string s = ss.str(); 这几乎是一行代码 - Kotauskas
2
但是...这是三行 - Gulzar

97
5年来,没人提到过.append吗?
#include <string>

std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");

或者在一行上:
s.append("Hello world, ").append("nice to see you, ").append("or not.");

13
s.append("One"); s.append(" line"); - Jonny
25
@Jonny s.append("One").append(" expression"); 或许我应该编辑原始代码以这种方式使用返回值?(该语句为Java代码,意为将字符串"One"和" expression"依次添加到字符串变量s中,并在后面加上分号;问题是是否应该使用返回值来更好地利用该操作的结果。) - Eponymous
@Eponymous 这不是一行代码,因为你忘记声明 s 变量了。 - slyy2048
8
OP在等效的C#代码和无法编译的C++代码中不同行声明了s。他所期望的C++代码是s += "Hello world, " + "nice to see you, " + "or not.";,可以写成s.append("Hello world, ").append("nice to see you, ").append("or not."); - Eponymous
7
append 的一个重要优势是它可以在字符串包含 NUL 字符时工作。 - John S.
显示剩余2条评论

81
s += "Hello world, " + "nice to see you, " + "or not.";

那些字符数组文字不是C++的std::strings - 你需要将它们转换:
s += string("Hello world, ") + string("nice to see you, ") + string("or not.");

要将整数(或任何其他可流式化类型)转换,您可以使用boost lexical_cast或提供自己的函数:

template <typename T>
string Str( const T & t ) {
   ostringstream os;
   os << t;
   return os.str();
}

现在您可以这样说:

string s = string("The meaning is ") + Str( 42 );

24
你只需要明确地将第一个字符串进行转换:s += string("Hello world,") + "nice to see you, " + "or not."。 - Ferruccio
10
是的,但我无法面对解释原因! - anon
1
boost::lexical_cast - 很好,类似于您的 Str 函数 :) - bayda
6
在构造函数string("Hello world")右侧执行的连接操作是通过定义在string类中的operator+()来完成的。如果表达式中没有string对象,则连接变成了char*指针的简单求和。 - davide
1
你可以使用C++字符串字面值"Hello world, "s,而不是string("Hello world, ") - Noa Sakurajin
显示剩余2条评论

44

你的代码可以写成1

s = "Hello world," "nice to see you," "or not."

...但我怀疑那不是你要找的。在你的情况下,你可能正在寻找流:

std::stringstream ss;
ss << "Hello world, " << 42 << "nice to see you.";
std::string s = ss.str();

1 "可写为":这仅适用于字符串字面量。 连接是由编译器完成的。


13
第一个例子值得一提,但请注明它仅适用于“连接”字面字符串(编译器会自行执行连接操作)。 - j_random_hacker
第一个例子在我之前声明了一个字符串,例如 const char smthg[] = "smthg",会触发一个错误 :/ 这是一个 bug 吗? - Hi-Angel
很遗憾,你不能这样做。不过,你可以使用#define来解决这个问题,但是这会带来它自己的问题。 - c z

35

使用 C++14 用户自定义字面量和 std::to_string,代码会变得更加简单。

using namespace std::literals::string_literals;
std::string str;
str += "Hello World, "s + "nice to see you, "s + "or not"s;
str += "Hello World, "s + std::to_string(my_int) + other_string;

请注意,将字符串文本串联可以在编译时完成。只需删除+即可。

str += "Hello World, " "nice to see you, " "or not";

3
自从C++11以来,您可以使用std::to_string。 - Patricio Rossi
用户定义的字面量也自C++11起存在。我已经编辑过了。 - Stack Danny
1
@StackDanny 这个改变是错误的。当我说“C++14”时,我指的是 std::literals::string_literals,而不是 UDL 的概念。 - Rapptz

29
在C++20中,你可以这样做:
auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

直到 std::format 可用之前,您可以使用 {fmt} 库 来实现相同的功能。
auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

免责声明:我是{fmt}和C++20 std::format的作者。

19
为了提供更加简洁的解决方案:可以实现一个名为concat的函数,将基于"经典"stringstream的解决方案简化为一行代码。它基于可变模板和完美转发。

用法:

std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);

实现:

void addToStream(std::ostringstream&)
{
}

template<typename T, typename... Args>
void addToStream(std::ostringstream& a_stream, T&& a_value, Args&&... a_args)
{
    a_stream << std::forward<T>(a_value);
    addToStream(a_stream, std::forward<Args>(a_args)...);
}

template<typename... Args>
std::string concat(Args&&... a_args)
{
    std::ostringstream s;
    addToStream(s, std::forward<Args>(a_args)...);
    return s.str();
}

如果在大型代码库中有多种不同的组合,那么这不会成为编译时膨胀吗? - Shital Shah
1
@ShitalShah,与其手动编写这些内联内容,不如使用这些辅助函数,因为这些函数最终也会被内联。 - underscore_d

8
auto s = string("one").append("two").append("three")

7
实际问题是在C++中使用“+”连接字符串文字会出错:
``` string s; s += "Hello world, " + "nice to see you, " + "or not."; ```
上面的代码会产生错误。
在C++(以及C)中,您只需将字符串文字放在彼此旁边即可将它们串联起来。
string s0 = "Hello world, " "nice to see you, " "or not.";
string s1 = "Hello world, " /*same*/ "nice to see you, " /*result*/ "or not.";
string s2 = 
    "Hello world, " /*line breaks in source code as well as*/ 
    "nice to see you, " /*comments don't matter*/ 
    "or not.";

如果你在宏中生成代码,这是有意义的:

#define TRACE(arg) cout << #arg ":" << (arg) << endl;

…一个可以像这样使用的简单宏

int a = 5;
TRACE(a)
a += 7;
TRACE(a)
TRACE(a+7)
TRACE(17*11)

(演示...)

或者,如果您坚持使用+来连接字符串字面量(如underscore_d所建议的):

string s = string("Hello world, ")+"nice to see you, "+"or not.";

另一种解决方案是在每个连接步骤中结合一个字符串和一个const char*

string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";

我也经常使用这种技术,但如果一个或多个变量是int/string类型怎么办?例如:string s = "abc" "def" (int)y "ghi" (std::string)z "1234"; 那么,sprintf仍然是最好的或最差的解决方案。 - Bart Mensfort
@BartMensfort 当然可以使用sprintf,但也有std::stringstream这个选项,它可以避免缓冲区过小的问题。 - Wolf

7

boost::format

or std::stringstream

std::stringstream msg;
msg << "Hello world, " << myInt  << niceToSeeYouString;
msg.str(); // returns std::string object

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