一次性连接多个C++字符串?

3
我对C++的字符串不太熟悉。有没有办法一次连接多个字符串?还是只能每次连接两个字符串?我的担心是每次操作可能需要分配内存,而不是仅在最终结果中分配一次。

我记得Java里有类似的功能,不知道std库中是否有类似的方法。实际上,std::stringstream可能就是这种方法,但我不知道它的具体用法。


3
除非您有理由不这样做(例如,您对性能进行了分析并发现其无法接受),否则只需像这样使用 operator +auto foo = string1 + string2 + "some text" + string3; - NathanOliver
你只是想要连接一堆随机字符串吗?如果是的话,那就直接使用“+”运算符。 - Shankha057
6个回答

9
你可以预先保留所需的空间,避免重新分配。
std::string s0{/*...*/}, s1{/*...*/}, s2{/*...*/};
std::string sink;

sink.reserve(s0.size() + s1.size() + s2.size() + 1);
sink += s0;
sink += s1;
sink += s2;

您可以使用可变参数的 string_cat 函数使其更加简洁。这是 C++17 的实现:

template <typename... Strings>
std::string string_cat(Strings&&... strings)
{
    std::string result;
    result.reserve((strings.size() + ...) + 1);
    ((result += std::forward<Strings>(strings)), ...);
    return result;
}

使用方法:

using namespace std::literals;
auto res = string_cat("a"s, "b"s, "c"s);

在wandbox上的实时示例


6

为避免昂贵的内存浪费,请使用 stringstream

std::stringstream 可能是这个,但我不知道它具体的工作原理。

它的工作方式如下:包含头文件 sstream,创建一个对象,使用流运算符<< 将需要的内容添加到对象中,使用函数 str 将结果作为字符串获取。

例如:

#include <sstream>
std::stringstream mySS;

mySS << "Hello" << " World!" << "end!!" << " Foo";

当您完成时
std::string stringResult = mySS.str();

所以它实际上会保留所有字符串,直到调用.str()方法?这就是我想知道的。谢谢。 - Viktor Smirnov
你可以像Java中的StringBuilder一样,不断地添加内容,直到需要时再调用str()方法将其转换为字符串。 - ΦXocę 웃 Пepeúpa ツ
抱歉多问一句,我想知道如何检查它是否像那样工作。我猜是在调试器中打开它? - Viktor Smirnov

3

您说得对,每个字符串拼接可能需要分配内存。考虑以下表达式,其中每个变量都是一个字符串:

a = b + c + d + e;

这需要三个连接操作和三个临时变量,每个都需要新的分配。

一个简单的解决方案是使用 std::ostringstream,它不应该需要太多重新分配:

std::ostringstream ss;
ss << b << c << d << e;
a = ss.str();

然而,如果我们只是要连接字符串,我们可以更好地分配恰好合适的字符串大小(符合 C++11 标准实现):

std::size_t total_string_size()
{
    return 1;
}

template <typename... T>
std::size_t total_string_size(std::string const &s, T const & ...tail)
{
    return s.size() + total_string_size(tail...);
}

void concat_strings_impl(std::string &) { }

template <typename... T>
void concat_strings_impl(std::string &out, std::string const &s, T const & ...tail)
{
    out += s;
    concat_strings_impl(out, tail...);
}

template <typename... T>
void concat_strings(std::string &out, T const & ...strings)
{
    out.clear();
    out.reserve(total_string_size(strings...));
    concat_strings_impl(out, strings...);
}

现在我们可以调用concat_strings(a, b, c, d, e),以单个重新分配的方式执行与a = b + c + d + e;等效的操作。(演示

你为什么认为std::stringstream不会使用太多的内存分配? - Galik
@Galik 我的意思是重新分配内存。它应该像std::vector一样分配一些额外的空间,而简单的+连接操作则需要为字符串临时变量进行新的分配。因此,更准确地说,stringstream不需要更多的重新分配,但通常应该需要更少的重新分配。 - cdhowie

2

避免多次分配内存的唯一方法是在连接字符串之前告诉字符串需要变成多大。

例如,将存储在向量(类似于列表)中的所有字符串连接起来:

std::string join(std::vector<std::string> const& v)
{
    std::string r; // return string

    // add up all the string sizes
    std::size_t size = 0;
    for(auto const& s: v)
        size += s.size();

    // reserve that much space all at once (one allocation)
    r.reserve(size);

    // now do the concatenations
    for(auto const& s: v)
        r += s;

    return r;
}

1
如果您正在使用C风格的字符串(即char*),那么您可以分配一次并连接多次。函数strcat将指向目标地址的指针作为第一个参数。因此,如果您使目标字符串足够大,则只需要进行一次分配。因此,
char* dest = new char[100];
dest[0] = 0; //Zero length string to start
strcat(dest, str1);
strcat(dest, str2);
strcat(dest, str3);

如果你使用 std::string,那么可以链接使用 +,例如 string1 + string2 + string3

他的问题似乎是关于多个字符串连接的性能 - 是否会为链中的每个步骤创建一个新的中间字符串,而不是一次性将它们全部组合起来? - Barmar
小心Schlemiel the Painter算法。 - Fred Larson

1

你可以连接多个字符串 - 没有问题:

std::string hello = "Hello";
std::string cruel = "cruel";
std::string world = "world";
std::string result = hello + " " + cruel + " " + world;

result 中存储了字符串 "Hello cruel world"。


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