169

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

上面的代码会产生错误。

4

24 回答 24

272
#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 的字符串格式化程序

于 2009-03-19T16:27:20.110 回答
85

5年内没人提过.append

#include <string>

std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");
于 2014-11-14T08:45:19.053 回答
76
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 );
于 2009-03-19T16:26:23.873 回答
43

您的代码可以写为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 "可以写成" :这仅适用于字符串文字。连接由编译器完成。

于 2009-03-19T16:28:19.937 回答
30

使用 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";
于 2014-07-01T20:00:58.223 回答
21

在 C++20 中,您将能够:

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

在此之前,您可以对{fmt} 库做同样的事情:

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

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

于 2018-04-29T17:25:54.697 回答
18

提供更单行的解决方案: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();
}
于 2014-05-28T11:21:10.310 回答
7

提升::格式

或 std::stringstream

std::stringstream msg;
msg << "Hello world, " << myInt  << niceToSeeYouString;
msg.str(); // returns std::string object
于 2009-03-19T16:26:33.920 回答
6

实际问题是在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.";

另一种解决方案const char*为每个连接步骤组合了一个字符串和一个

string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";
于 2017-09-19T09:25:36.743 回答
6
auto s = string("one").append("two").append("three")
于 2017-10-07T03:58:05.147 回答
3

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

妈的,快50秒了……

于 2009-03-19T16:28:09.143 回答
3

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

string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
于 2015-09-10T16:37:46.430 回答
3

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

这是另一个使用 C++11 lambdafor_each并允许提供separator分隔字符串的解决方案:

#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
于 2016-02-22T16:21:27.287 回答
3

这是单线解决方案:

#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::string,然后使用(从左到右)计算顺序 ofoperator+确保其操作数始终为 a std::string。以这种方式,我们std::string将左侧的 与const char *右侧的操作数连接起来并返回另一个std::string,级联效果。

注意:右操作数有几个选项,包括const char *std::stringchar

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

于 2018-10-01T18:21:45.593 回答
2

也许您喜欢我的“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;
}
于 2016-08-31T15:57:19.487 回答
1

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

using namespace concat;

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

在引擎盖下,您将使用 std::ostringstream。

于 2014-07-23T07:21:46.053 回答
1

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

主要代码

#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
于 2016-01-02T21:34:25.423 回答
1

像这样的东西对我有用

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);
于 2017-10-15T09:24:29.750 回答
1

基于上述解决方案,我为我的项目创建了一个类 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++ 中是否会有更好的东西?

于 2017-11-02T10:19:03.310 回答
1

在 c11 中:

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

这允许您像这样创建函数调用:

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

将打印:消息编号:10

于 2017-11-10T21:24:48.587 回答
0

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

这是使用 operator<< 显示与流没有冲突的代码

注意:如果您取消注释 s1.reserve(30),则只有 3 个 new() 运算符请求(1 个用于 s1,1 个用于 s2,1 个用于保留;不幸的是,您不能在构造函数时保留);没有保留,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;
}
于 2016-03-10T15:23:17.190 回答
0

带有使用 lambda 函数的简单预处理宏的 Stringstream 看起来不错:

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

进而

auto str = make_string("hello" << " there" << 10 << '$');
于 2019-05-15T00:36:17.363 回答
-1

这对我有用:

#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
于 2013-09-25T17:36:43.797 回答
-1

您是否尝试避免使用 +=?而是使用 var = var + ... 它对我有用。

#include <iostream.h> // for string

string myName = "";
int _age = 30;
myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;
于 2019-01-16T13:04:28.870 回答