如何在 C++ 中添加多个字符串

Posted

技术标签:

【中文标题】如何在 C++ 中添加多个字符串【英文标题】:How to add many strings in c++ 【发布时间】:2011-01-19 01:02:52 【问题描述】:

据我所知,C++ 只允许将 2 个字符串加在一起,即: s = s1 + s2

但是我怎样才能将许多字符串加在一起呢?喜欢:

s = s1 + s2 + s3 + s4 + ... + sn

【问题讨论】:

为什么你认为这行不通? 你使用的是字符串类吗? 是的,我注意到它仅在我使用字符串类时才有效。但我想在 C++ 中做这样的事情 #define St "blah3" s = s1 + "blah1" + "blah2" + St @tsubasa:嗯,你不能。如果您在编译时知道两个文字,只需将它们彼此相邻放置即可让它们在预处理器中连接。即,"asd" "123" 变为 "asd123"。但是在运行时添加字符串需要你使用字符串类。 @Billy:好吧,它本身不会添加指针值。它会尝试但编译失败。 【参考方案1】:

如果您尝试附加 std::string 类的字符串对象,这应该可以。

string s1 = "string1";
string s2 = "string2";
string s3 = "string3";

string s = s1 + s2 + s3;

string s = string("s1") + string("s2") + string("s3") ...

【讨论】:

+1 - 请记住,当您使用 operator+(std::basic_string<t>, std::basic_string<t>) 时,您将在 C++03 中产生指数时间。相比之下,使用std::basic_string<t>::append 成员函数只需要线性时间。 我认为 string s = string("s1") + string("s2") + string("s3") ... 是我正在寻找的东西。我想知道为什么它会起作用? 因为你在使用 string(x) 时,你调用了 x 上标准字符串类的构造函数。您创建一个临时字符串对象,然后可以参与它的 operator+。 (这种工作方式类似于演员表) @tsubasa:只要确保第一项是string 就足够了,所以string("s1") + "s2" + "s3" 也可以。原因是像"s1" 这样的普通字符串常量是const char* 类型的,你不能把这样的指针加在一起。另一方面,string 对象知道如何添加const char* 以形成新的string。 (另外:这里没有什么会使用指数时间,所以不要太担心) @BillyONeal:你确定这是指数时间吗?给我看 O(N^2)。【参考方案2】:

首先,你可以做 +sn 的事情就好了。虽然假设您在 C++03 上使用 std::basic_string<t> 字符串,但它会花费 exponential 二次(参见 cmets)时间。

您可以使用 std::basic_string<t>::appendstd::basic_string<t>::reserve 在 O(n) 时间内连接您的字符串。

编辑:例如

string a;
//either
a.append(s1).append(s2).append(s3);
//or
a.append("I'm a string!").append("I am another string!");

【讨论】:

它不需要指数时间,只需要二次。 append 会更快,但总的来说是二次的,因为它需要不时重新分配表格。不过,在大多数情况下,这两种方法都不会慢到让人注意到。 @BillyONEal:为什么会是指数级的?您提到的总和等于(0*N + N) + (1*N + N) + (2*N + N) + ... + ((K-1)*N + N),等于(1+2+...+(K-1)) * N + K*N = (K*(K-1)/2) * N + K*N = ((K^2 + K) / 2) * N = O(K^2 * N)。所以它的零件数量是二次方K。在每个部分的字符数上,N,是线性的。 关于append():我好像错过了你提到的reserve()。与reserve() 一起使用,它确实是线性的。 @sth 即使没有预留足够的空间,append 也会采用比二次方快得多的线性摊销时间 @BillyONeal 您可以声称它是指数的,其中有问题的指数恰好是 2(诚然这仍然不是很好:P)【参考方案3】:
s = s1 + s2 + s3 + .. + sn;

虽然会创建很多临时变量(一个好的优化编译器应该会有所帮助),但它会起作用,因为它会被有效地解释为:

string tmp1 = s1 + s2;
string tmp2 = tmp1 + s3;
string tmp3 = tmp2 + s4;
...
s = tmpn + sn;

保证不会创建临时对象的另一种方法是:

s = s1;
s += s2;
s += s3;
...
s += sn;

【讨论】:

你的例子不太一样 tmp1 必须是复制结构。如果 tmp1 是 'string const&' 也是一样的。虽然我不得不承认 RVO 可能会消除副本。【参考方案4】:

std::ostringstream 就是为此而构建的,参见示例 here。很简单:

std::ostringstream out;
out << "a" << "b" << "c" << .... << "z";
std::string str( out.str());

【讨论】:

我会指出,使用 stringstream 附加字符串比仅附加到字符串要慢。 stringstream 非常适合将事物转换为字符串,但如果您所做的只是从字符串构造字符串,那么它是一种效率较低的方法。 append() 和 += 是要走的路。 其实我最近为它写了一个测试程序,发现用stringstream把字符串放在一起比直接追加到字符串要花大约2 1/2倍的时间——那是没有 完成后从字符串流中取出字符串。在不同的情况下,该特定数字可能并不准确,但很明显,使用 stringstream 构造流比仅附加到字符串要慢得多。 好吧,我做了一些测试,发现虽然稍微慢了一点,但只慢了 20%。取一个字符串并在其上添加 10 个包含 10 个字符的字符串。重置并重复此操作一百万次。差异是 2 秒(在我的机器上)。几乎不值得注意,因为任何处理器停顿都会立即消除这种差异。我在这方面的胜利是内存管理系统的开销较低。由于字符串流具有相对较大的缓冲区,因此分配空间的调用较少。 这可能是由于内部字符串缓冲区管理(指数增长?)和字符串流缓冲区管理的特定实现。两者都不能保证以一种或另一种方式行事。 @Nikolai True。如果您真的很在意并且想要确定哪个更快,您需要针对您正在使用的特定实现处理您正在使用的特定程序来分析它。但是,一般迹象是 stringstream 可能会更慢,所以我会说使用 string 的 append() 或 += 除非您需要 stringstream 的特殊功能,或者您真的关心性能并分析了您的代码并发现在您的特定情况下,该字符串流更好。【参考方案5】:

使用模板添加字符串,char*和char's组成字符串

strlen:-

#include <iostream>
#include <cstring>

// it_pair to wrap a pair of iterators for a for(:) loop
template<typename IT> 
class it_pair
    
    IT b;
    IT e;
public:
    auto begin() const
        
        return b;
        
    auto end() const
        
        return e;
        
    ;

// string length
template<typename S> auto strlen(const S& s) -> decltype(s.size())
    
    return s.size();
    

auto strlen(char c) -> size_t
    
    return 1u;
    

auto strlen(const std::initializer_list<char>& il) -> size_t
    
    return il.size();
    

template<typename IT>
auto strlen(const it_pair<IT>& p)
    
    auto len = size_t;
    for(const auto& s:p)
        len += strlen(s);
    return len;
    

template<typename S, typename ...SS> auto strlen(S s, SS&... ss) -> size_t
    
    return strlen(s) + strlen(ss...);
    

附加字符串

// terminate recursion
template<typename TA, typename TB>
void append(TA& a, TB& b)
    
    a.append(b);
    

// special case for a character
template<>
void append<std::string, const char>(std::string& a, const char& b)
    
    a.append(1, b);
    

// special case for a collection of strings
template<typename TA, typename TB>
void append(TA& a, const it_pair<TB>& p)
    
    for(const auto& x: p)
        a.append(x);
    

// recursion append
template<typename TA, typename TB, typename ...TT>
void append(TA& a, TB& b, TT&... tt)
    
    append(a, b);
    append(a, tt...);
    

template<typename ...TT>
std::string string_add(const TT& ... tt)
    
    std::string s;
    s.reserve(strlen(tt...));
    append(s, tt...);
    return s;
    

template<typename IT>
auto make_it_pair(IT b, IT e)
    
    return it_pair<IT>b, e;
    

template<typename T>
auto make_it_pair(const T& t)
    
    using namespace std;
    return make_it_pair(cbegin(t), cend(t));
    

主要例子

int main()
    
    const char * s[] = "vw", "xyz";
    std::vector<std::string> v"l", "mn", "opqr";
    std::string a("a");
    std::string b("bc");
    std::string c("def");
    std::cout << string_add(a, b+c, "ghij", make_it_pair(v), 'k', make_it_pair(s));
    

【讨论】:

你应该创建一个标准化提案:)【参考方案6】:

如果你想这样做

Efficiently,即不产生二次时间 不覆盖原来的变量 不创建临时变量

这样就可以了

auto s = std::string(s1).append(s2).append(s3).append(sn);  

如果你喜欢格式很好的东西

auto s = std::string(s1).append(s2)
                        .append(s3)
                        .append(sn)

【讨论】:

以上是关于如何在 C++ 中添加多个字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何从字符串 C++ 中读取多个整数

C++——如何将一个字符串转换为多个整数?

如何在 C++ 中使用 HDF5 存储多个 2D 字符数组?

如何在 C++ 中向字符串中添加字符?

如何从 C++ 中的字符串中快速查找和子串多个项目?

如何在 C++ 中将空格分隔的字符串拆分为多个字符串?