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

How do I concatenate multiple C++ strings on one line?

提问人:Nick Bolton 提问时间:3/20/2009 最后编辑:GulzarNick Bolton 更新时间:11/18/2023 访问量:504777

问:

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.";

上面的代码产生错误。

C++ 字符串 编译器错误 串联 单行

评论

7赞 underscore_d 12/4/2015
正如在其他地方解释的那样,这并不是因为“它不支持带有 + 运算符的多个字符串/变量”——而是因为您正在尝试相互添加指针。这就是产生错误的原因 - 因为求和指针是无意义的。如下所述,至少将第一个操作数转换为 ,并且完全没有错误。char *std::string
1赞 Wolf 9/19/2017
产生了哪个错误?
0赞 vitaut 4/30/2018
如何连接 std::string 和 int 的可能重复?

答:

81赞 anon 3/20/2009 #1
s += "Hello world, " + "nice to see you, " + "or not.";

这些字符数组文字不是 C++ std::strings - 您需要转换它们:

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

要转换 ints(或任何其他可流式处理的类型),您可以使用 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赞 Ferruccio 3/20/2009
你只需要显式转换第一个: s += string(“Hello world,”) + “很高兴见到你, ” + “or not.”;
10赞 3/20/2009
是的,但我无法解释为什么!
1赞 bayda 3/20/2009
boost::lexical_cast - 在您的 Str 函数上很好且相似:)
6赞 davide 12/12/2015
在构造函数右侧完成的连接是通过类中定义的。如果表达式中没有对象,则串联将变成 char 指针的总和。string("Hello world")operator+()stringstringchar*
1赞 Noa Sakurajin 3/7/2023
您可以使用 c++ 字符串文字代替string("Hello world, ")"Hello world, "s
7赞 bayda 3/20/2009 #2

boost::格式

或者 std::stringstream

std::stringstream msg;
msg << "Hello world, " << myInt  << niceToSeeYouString;
msg.str(); // returns std::string object
290赞 Paolo Tedesco 3/20/2009 #3
#include <sstream>
#include <string>

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

看看这篇来自 Herb Sutter 的本周大师文章:庄园农场的字符串格式化程序

评论

7赞 Byzantian 1/7/2013
试试这个:std::string s = static_cast<std::ostringstream&>(std::ostringstream().seekp(0) << "HelloWorld" << myInt << niceToSeeYouString).str();
57赞 joaerl 1/24/2014
ss << “哇,C++中的字符串连接令人印象深刻”<<“或”不”。
5赞 Patricio Rossi 11/12/2017
换一种说法:使用多个 append: string s = string(“abc”).append(“def”).append(otherStrVar).append(to_string(123));
1赞 Kotauskas 8/7/2018
std::stringstream ss; ss << "Hello, world, " << myInt << niceToSeeYouString; std::string s = ss.str();几乎是一行
2赞 Gulzar 12/1/2021
但。。这是 3 行
3赞 tstenner 3/20/2009 #4

您必须为要浓缩到字符串的每种数据类型定义 operator+(),但由于 operator<< 是为大多数类型定义的,因此您应该使用 std::stringstream。

该死的,被打败了 50 秒......

评论

1赞 Tyler McHenry 3/20/2009
实际上,您无法在 char 和 int 等内置类型上定义新的运算符。
1赞 Eponymous 10/19/2015
@TylerMcHenry 在这种情况下,我并不推荐它,但你当然可以:std::string operator+(std::string s, int i){ return s+std::to_string(i); }
44赞 John Dibling 3/20/2009 #5

你的代码可以写成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();

1can be written as” :这仅适用于字符串文字。串联由编译器完成。

评论

13赞 j_random_hacker 3/20/2009
您的第一个示例值得一提,但也请提及它仅适用于“连接”文字字符串(编译器本身执行连接)。
0赞 Hi-Angel 2/19/2015
第一个示例触发了我一个错误,如果之前将字符串声明为 :/这是一个错误吗?const char smthg[] = "smthg"
0赞 c z 7/8/2020
@Hi-Angel 不幸的是,你可以用你的字符串来解决这个问题,尽管这会带来自己的问题。#define
-1赞 erik80 9/26/2013 #6

这对我有用:

#include <iostream>

using namespace std;

#define CONCAT2(a,b)     string(a)+string(b)
#define CONCAT3(a,b,c)   string(a)+string(b)+string(c)
#define CONCAT4(a,b,c,d) string(a)+string(b)+string(c)+string(d)

#define HOMEDIR "c:\\example"

int main()
{

    const char* filename = "myfile";

    string path = CONCAT4(HOMEDIR,"\\",filename,".txt");

    cout << path;
    return 0;
}

输出:

c:\example\myfile.txt

评论

12赞 Rui Marques 5/21/2014
每当有人将宏用于比代码守卫或常量更复杂的事情时,小猫就会哭泣:P
1赞 SebastianK 6/2/2014
除了不快乐的小猫:对于每个参数,都会创建一个字符串对象,这不是必需的。
2赞 dhaumann 11/18/2015
投了反对票,因为使用宏绝对是一个糟糕的解决方案
0赞 underscore_d 12/4/2015
即使对于 C 来说,这也会让我吓得退缩,但在 C++ 中,这是恶魔般的。@RuiMarques:在哪些情况下,宏对于常量来说比 A(如果要求零存储)更好?constenum
0赞 Rui Marques 12/4/2015
@underscore_d有趣的问题,但我没有答案。也许答案是否定的。
19赞 SebastianK 5/28/2014 #7

要提供更单行的解决方案:可以实现一个函数,将基于“经典”字符串流的解决方案简化为单个语句。 它基于可变模板和完美的转发。concat


用法:

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();
}

评论

0赞 Shital Shah 2/8/2017
如果大型代码库中有几种不同的组合,这不会成为编译时膨胀。
1赞 underscore_d 10/14/2018
@ShitalShah只不过是手动内联地编写这些东西,因为这些辅助函数无论如何都会内联。
35赞 Rapptz 7/2/2014 #8

使用 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赞 Patricio Rossi 11/12/2017
从 C++11 开始,您可以使用 std::to_string
0赞 Stack Danny 5/3/2019
自 C++11 <>以来,用户定义的文字也是如此。我编辑了。
1赞 Rapptz 5/4/2019
@StackDanny 更改是错误的。当我说“C++14”时,我指的是 ,而不是 UDL 的概念。std::literals::string_literals
1赞 José Manuel 7/23/2014 #9

您可以为此使用此标头:https://github.com/theypsilon/concat

using namespace concat;

assert(concat(1,2,3,4,5) == "12345");

在后台,您将使用 std::ostringstream。

97赞 Michel 11/14/2014 #10

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.");

评论

0赞 Hi-Angel 2/19/2015
因为与仅在一行中添加文本相比,这很麻烦。
13赞 Jonny 8/27/2015
s.append("One"); s.append(" line");
25赞 Eponymous 9/11/2015
@Jonny 也许我应该编辑原始文件以这种方式使用返回值?s.append("One").append(" expression");
8赞 Eponymous 10/19/2015
@SilverMöls OP 在等效的 C# 代码和其非编译C++代码中以不同的行声明。他想要的 C++ 是可以编写的ss += "Hello world, " + "nice to see you, " + "or not.";s.append("Hello world, ").append("nice to see you, ").append("or not.");
7赞 John S. 3/25/2018
一个主要优点是,当字符串包含 NUL 字符时,它也可以工作。append
3赞 Eponymous 9/11/2015 #11

如果写出 ,它看起来和 C 几乎一样#+=

string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
1赞 Scindix 1/3/2016 #12

如果您愿意使用,则可以使用用户定义的字符串文字并定义两个函数模板,这些模板会重载对象和任何其他对象的加号运算符。唯一的缺陷是不要重载 的加号运算符,否则编译器不知道要使用哪个运算符。您可以使用 type_traits 中的模板 std::enable_if 来执行此操作。之后,字符串的行为就像在 Java 或 C# 中一样。有关详细信息,请参阅我的示例实现。c++11std::stringstd::string

主代码

#include <iostream>
#include "c_sharp_strings.hpp"

using namespace std;

int main()
{
    int i = 0;
    float f = 0.4;
    double d = 1.3e-2;
    string s;
    s += "Hello world, "_ + "nice to see you. "_ + i
            + " "_ + 47 + " "_ + f + ',' + d;
    cout << s << endl;
    return 0;
}

文件c_sharp_strings.hpp

将此头文件包含在要包含这些字符串的所有位置。

#ifndef C_SHARP_STRING_H_INCLUDED
#define C_SHARP_STRING_H_INCLUDED

#include <type_traits>
#include <string>

inline std::string operator "" _(const char a[], long unsigned int i)
{
    return std::string(a);
}

template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
                        !std::is_same<char, T>::value &&
                        !std::is_same<const char*, T>::value, std::string>::type
operator+ (std::string s, T i)
{
    return s + std::to_string(i);
}

template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
                        !std::is_same<char, T>::value &&
                        !std::is_same<const char*, T>::value, std::string>::type
operator+ (T i, std::string s)
{
    return std::to_string(i) + s;
}

#endif // C_SHARP_STRING_H_INCLUDED
3赞 elnigno 2/23/2016 #13

正如其他人所说,OP 代码的主要问题是运算符不连接;不过,它适用于 。+const char *std::string

这是另一个使用 C++ lambda 并允许提供 a 来分隔字符串的解决方案:for_eachseparator

#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>

string join(const string& separator,
            const vector<string>& strings)
{
    if (strings.empty())
        return "";

    if (strings.size() == 1)
        return strings[0];

    stringstream ss;
    ss << strings[0];

    auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
    for_each(begin(strings) + 1, end(strings), aggregate);

    return ss.str();
}

用法:

std::vector<std::string> strings { "a", "b", "c" };
std::string joinedStrings = join(", ", strings);

它似乎可以很好地扩展(线性),至少在我的计算机上进行了快速测试之后;这是我写的一个快速测试:

#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <chrono>

using namespace std;

string join(const string& separator,
            const vector<string>& strings)
{
    if (strings.empty())
        return "";

    if (strings.size() == 1)
        return strings[0];

    stringstream ss;
    ss << strings[0];

    auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
    for_each(begin(strings) + 1, end(strings), aggregate);

    return ss.str();
}

int main()
{
    const int reps = 1000;
    const string sep = ", ";
    auto generator = [](){return "abcde";};

    vector<string> strings10(10);
    generate(begin(strings10), end(strings10), generator);

    vector<string> strings100(100);
    generate(begin(strings100), end(strings100), generator);

    vector<string> strings1000(1000);
    generate(begin(strings1000), end(strings1000), generator);

    vector<string> strings10000(10000);
    generate(begin(strings10000), end(strings10000), generator);

    auto t1 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings10);
    }

    auto t2 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings100);
    }

    auto t3 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings1000);
    }

    auto t4 = chrono::system_clock::now();
    for(int i = 0; i<reps; ++i)
    {
        join(sep, strings10000);
    }

    auto t5 = chrono::system_clock::now();

    auto d1 = chrono::duration_cast<chrono::milliseconds>(t2 - t1);
    auto d2 = chrono::duration_cast<chrono::milliseconds>(t3 - t2);
    auto d3 = chrono::duration_cast<chrono::milliseconds>(t4 - t3);
    auto d4 = chrono::duration_cast<chrono::milliseconds>(t5 - t4);

    cout << "join(10)   : " << d1.count() << endl;
    cout << "join(100)  : " << d2.count() << endl;
    cout << "join(1000) : " << d3.count() << endl;
    cout << "join(10000): " << d4.count() << endl;
}

结果(毫秒):

join(10)   : 2
join(100)  : 10
join(1000) : 91
join(10000): 898
0赞 ddy 3/10/2016 #14

您还可以“扩展”字符串类并选择您喜欢的运算符(<<、&、|等......

下面是使用 operator<< 的代码,以显示与流没有冲突

注意:如果你取消注释 s1.reserve(30),则只有 3 个 new() 运算符请求(1 个用于 s1,1 个用于 s2,1 个用于 reserve;不幸的是,你不能在构造函数时保留);如果没有保留,S1 在增长时必须请求更多内存,因此这取决于您的编译器实现增长因子(在本例中,我的似乎是 1.5、5 个 new() 调用)

namespace perso {
class string:public std::string {
public:
    string(): std::string(){}

    template<typename T>
    string(const T v): std::string(v) {}

    template<typename T>
    string& operator<<(const T s){
        *this+=s;
        return *this;
    }
};
}

using namespace std;

int main()
{
    using string = perso::string;
    string s1, s2="she";
    //s1.reserve(30);
    s1 << "no " << "sunshine when " << s2 << '\'' << 's' << " gone";
    cout << "Aint't "<< s1 << " ..." <<  endl;

    return 0;
}
2赞 bterwijn 8/31/2016 #15

也许你喜欢我的“Streamer”解决方案,真正在一行中完成:

#include <iostream>
#include <sstream>
using namespace std;

class Streamer // class for one line string generation
{
public:

    Streamer& clear() // clear content
    {
        ss.str(""); // set to empty string
        ss.clear(); // clear error flags
        return *this;
    }

    template <typename T>
    friend Streamer& operator<<(Streamer& streamer,T str); // add to streamer

    string str() // get current string
    { return ss.str();}

private:
    stringstream ss;
};

template <typename T>
Streamer& operator<<(Streamer& streamer,T str)
{ streamer.ss<<str;return streamer;}

Streamer streamer; // make this a global variable


class MyTestClass // just a test class
{
public:
    MyTestClass() : data(0.12345){}
    friend ostream& operator<<(ostream& os,const MyTestClass& myClass);
private:
    double data;
};

ostream& operator<<(ostream& os,const MyTestClass& myClass) // print test class
{ return os<<myClass.data;}


int main()
{
    int i=0;
    string s1=(streamer.clear()<<"foo"<<"bar"<<"test").str();                      // test strings
    string s2=(streamer.clear()<<"i:"<<i++<<" "<<i++<<" "<<i++<<" "<<0.666).str(); // test numbers
    string s3=(streamer.clear()<<"test class:"<<MyTestClass()).str();              // test with test class
    cout<<"s1: '"<<s1<<"'"<<endl;
    cout<<"s2: '"<<s2<<"'"<<endl;
    cout<<"s3: '"<<s3<<"'"<<endl;
}
7赞 Wolf 9/19/2017 #16

实际问题是在 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)

(现场演示 ...)

或者,如果您坚持使用 for 字符串文字(正如 underscore_d 已经建议的那样):+

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

另一种解决方案将每个连接步骤的字符串和 a 组合在一起const char*

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

评论

0赞 Bart Mensfort 11/2/2017
我也经常使用这种技术,但是如果一个或多个变量是 int/string 怎么办? 例如,字符串 s = “abc” “def” (int)y “ghi” (std::string)z “1234”;那么,Sprintf 仍然是最糟糕的解决方案中最好的。
0赞 Wolf 11/2/2017
@BartMensfort当然是一个选项,但也有 std::stringstream 可以防止缓冲区过小的问题。sprintf
8赞 Shital Shah 10/7/2017 #17
auto s = string("one").append("two").append("three")
1赞 smoothware 10/15/2017 #18

这样的东西对我有用

namespace detail {
    void concat_impl(std::ostream&) { /* do nothing */ }

    template<typename T, typename ...Args>
    void concat_impl(std::ostream& os, const T& t, Args&&... args)
    {
        os << t;
        concat_impl(os, std::forward<Args>(args)...);
    }
} /* namespace detail */

template<typename ...Args>
std::string concat(Args&&... args)
{
    std::ostringstream os;
    detail::concat_impl(os, std::forward<Args>(args)...);
    return os.str();
}
// ...
std::string s{"Hello World, "};
s = concat(s, myInt, niceToSeeYouString, myChar, myFoo);
1赞 Bart Mensfort 11/2/2017 #19

基于上述解决方案,我为我的项目制作了一个类var_string,让生活变得轻松。例子:

var_string x("abc %d %s", 123, "def");
std::string y = (std::string)x;
const char *z = x.c_str();

类本身:

#include <stdlib.h>
#include <stdarg.h>

class var_string
{
public:
    var_string(const char *cmd, ...)
    {
        va_list args;
        va_start(args, cmd);
        vsnprintf(buffer, sizeof(buffer) - 1, cmd, args);
    }

    ~var_string() {}

    operator std::string()
    {
        return std::string(buffer);
    }

    operator char*()
    {
        return buffer;
    }

    const char *c_str()
    {
        return buffer;
    }

    int system()
    {
        return ::system(buffer);
    }
private:
    char buffer[4096];
};

仍然想知道 C++ 中是否会有更好的东西?

1赞 devcodexyz 11/11/2017 #20

在 c11 中:

void printMessage(std::string&& message) {
    std::cout << message << std::endl;
    return message;
}

这允许您创建如下所示的函数调用:

printMessage("message number : " + std::to_string(id));

将打印 : 留言编号 : 10

29赞 vitaut 4/30/2018 #21

在 C++20 中,您可以执行以下操作:

auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

在可用之前,您可以对 {fmt} 库执行相同的操作:std::format

auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);

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

3赞 Apollys supports Monica 10/2/2018 #22

以下是单行解决方案:

#include <iostream>
#include <string>

int main() {
  std::string s = std::string("Hi") + " there" + " friends";
  std::cout << s << std::endl;

  std::string r = std::string("Magic number: ") + std::to_string(13) + "!";
  std::cout << r << std::endl;

  return 0;
}

虽然它有点丑,但我认为它和你猫在 C++ 中一样干净。

我们将第一个参数转换为 a,然后使用(从左到右)的求值顺序来确保其操作数始终为 .以这种方式,我们将左边的操作数与右边的操作数连接起来,然后返回另一个操作数,级联效果。std::stringoperator+std::stringstd::stringconst char *std::string

注意:正确的操作数有几个选项,包括 、 和 。const char *std::stringchar

由您决定幻数是 13 还是 6227020800。

评论

0赞 Mr.Zeus 12/16/2018
啊,你忘了,@Apollys,通用的魔术数字是 42。:D
1赞 Sebastian Mach 8/31/2020
奇怪的是,以“这是单行解决方案”开头你的答案,就好像你是唯一一个有单行答案的人(你不是;几个,近十年前的答案已经提供了单行)。其他答案只是没有明确提到它,因为单行是问题的前提,即“如何在一行上连接多个 C++ 字符串?你真的应该把你傲慢的语气调低一点。此外,您正在进行性能测试和其他“leet stuff”,但在每次输出后刷新您的输出缓冲区......
-1赞 vincent thorpe 1/16/2019 #23

你有没有试过避免+=? 改用 var = var + ... 它对我有用。

#include <iostream.h> // for string

string myName = "";
int _age = 30;
myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;

评论

0赞 vincent thorpe 1/17/2019
我使用 C++ borland builder 6,它对我来说效果很好。不要忘记包含此标头#include <iostream.h> // string #include <system.hpp> // ansiString
0赞 vincent thorpe 3/15/2019
在这种情况下,+= 没有重载,似乎认为您有添加数字而不是连接字符串
0赞 asikorski 5/15/2019 #24

使用 lambda 函数的简单 preproccessor 宏的 Stringstream 似乎不错:

#include <sstream>
#define make_string(args) []{std::stringstream ss; ss << args; return ss;}() 

然后

auto str = make_string("hello" << " there" << 10 << '$');
0赞 S.V 11/4/2023 #25

一行:

std::string s = std::string("Hello world, ") + std::to_string(888) + " nice to see you";

std::string s = std::string() + "Hello world, " + std::to_string(888) + " nice to see you";