【问题标题】:How do I concatenate multiple C++ strings on one line?如何在一行上连接多个 C++ 字符串?
【发布时间】:2010-10-14 08:34:50
【问题描述】:

C# 有一个语法特性,您可以在 1 行中将多种数据类型连接在一起。

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

以上代码产生错误。

【问题讨论】:

  • 正如在别处解释的那样,这不是因为“它不支持使用 + 运算符的多个字符串/变量” - 而是因为您试图将 char * 指针添加到彼此。这就是产生错误的原因 - 因为求和指针是无意义的。如下所述,至少将第一个操作数变成std::string,完全没有错误。
  • 产生了哪个错误?

标签: c++ string compiler-errors concatenation one-liner


【解决方案1】:
#include <sstream>
#include <string>

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

看看 Herb Sutter 的这篇 Guru Of The Week 文章:The String Formatters of Manor Farm

【讨论】:

  • 试试这个:std::string s = static_cast&lt;std::ostringstream&amp;&gt;(std::ostringstream().seekp(0) &lt;&lt; "HelloWorld" &lt;&lt; myInt &lt;&lt; niceToSeeYouString).str();
  • ss
  • 只是换一种方式命名:使用多个附加:string s = string("abc").append("def").append(otherStrVar).append(to_string(123));跨度>
  • std::stringstream ss; ss &lt;&lt; "Hello, world, " &lt;&lt; myInt &lt;&lt; niceToSeeYouString; std::string s = ss.str(); 几乎是一行
  • 但是.. 这是 3 行
【解决方案2】:

5 年内没有人提到.append

#include <string>

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

【讨论】:

  • 因为与仅在一行中添加文本相比很麻烦。
  • s.append("One"); s.append(" line");
  • @Jonny s.append("One").append(" expression"); 也许我应该编辑原件以这样使用返回值?
  • @SilverMöls 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.");
  • append 的一个主要优点是它在字符串包含 NUL 字符时也可以工作。
【解决方案3】:
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 );

【讨论】:

  • 你只需要显式转换第一个:s += string("Hello world,") + "nice to see you, " + "or not.";
  • 是的,但我无法解释原因!
  • boost::lexical_cast - 在你的 Str 函数上很好并且类似:)
  • 在构造函数string("Hello world") 右侧完成的连接是通过在类string 中定义的operator+() 执行的。如果表达式中没有string 对象,则连接将成为字符指针char* 的总和。
  • error: use of undeclared identifier 's'
【解决方案4】:

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

【讨论】:

  • 您的第一个示例值得一提,但请注意它仅适用于“连接”文字字符串(编译器自行执行连接)。
  • 如果一个字符串先前被声明为例如,第一个例子对我触发了一个错误。 const char smthg[] = "smthg" :/ 这是一个错误吗?
  • @Hi-Angel 不幸的是,你可以#define你的字符串来解决这个问题,尽管这会带来自己的问题。
【解决方案5】:

使用 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";

【讨论】:

  • 从C++11开始可以使用std::to_string
  • 用户定义的文字也是从 C++11 <> 开始的。我编辑了。
  • @StackDanny 更改是错误的。当我说“C++14”时,我指的是std::literals::string_literals,而不是 UDL 的概念。
【解决方案6】:

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

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

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

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

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

【讨论】:

    【解决方案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();
    }
    

    【讨论】:

    • 如果大型代码库中有几种不同的组合,这不会导致编译时膨胀。
    • @ShitalShah 只不过是手动编写内联的东西,因为这些辅助函数无论如何都会被内联。
    【解决方案8】:

    提升::格式

    或 std::stringstream

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

    【讨论】:

      【解决方案9】:

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

      (live demo ...)

      或者,如果您坚持使用 + 作为字符串文字(正如 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 怎么办? 。例如。字符串 s = "abc" "def" (int)y "ghi" (std::string)z "1234";那么,sprintf 仍然是最好的解决方案。
      • @BartMensfort 当然sprintf 是一个选项,但也有std::stringstream 可以防止缓冲区过小问题。
      【解决方案10】:
      auto s = string("one").append("two").append("three")
      

      【讨论】:

        【解决方案11】:

        您必须为要连接到字符串的每种数据类型定义 operator+(),但由于大多数类型都定义了 operator

        妈的,快50秒了……

        【讨论】:

        • 您实际上无法在 char 和 int 等内置类型上定义新的运算符。
        • @TylerMcHenry 在这种情况下我不推荐它,但你当然可以:std::string operator+(std::string s, int i){ return s+std::to_string(i); }
        【解决方案12】:

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

        string s("Some initial data. "); int i = 5;
        s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
        

        【讨论】:

          【解决方案13】:

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

          这是另一个使用 C++11 lambda 和 for_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
          

          【讨论】:

            【解决方案14】:

            这是单线解决方案:

            #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++ 中的猫一样干净。

            我们将第一个参数转换为std::string,然后使用operator+ 的(从左到右)计算顺序来确保其left 操作数始终是std::string。通过这种方式,我们将左侧的std::string 与右侧的const char * 操作数连接起来,并返回另一个std::string,级联效果。

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

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

            【讨论】:

            • 啊,你忘了,@Apollys,通用幻数是 42。:D
            • "Here's the one-liner solution" 开始你的回答很奇怪,好像你是唯一一个使用单线的人(你不是;几个,几乎十年前的答案已经提供了单行字)。其他答案只是没有明确提及它,因为单行是问题的前提,即 “如何在 一行上连接多个 C++ 字符串?”乙>。你真的应该把你傲慢的语气调低一点。此外,您正在进行性能测试和其他“leet 东西”,但在每次输出后刷新输出缓冲区......
            【解决方案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;
            }
            

            【讨论】:

              【解决方案16】:

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

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

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

              【讨论】:

                【解决方案17】:

                如果您愿意使用c++11,您可以使用user-defined string literals 并定义两个函数模板,为std::string 对象和任何其他对象重载加号运算符。唯一的陷阱是不要重载std::string的加号运算符,否则编译器不知道使用哪个运算符。您可以使用来自type_traits 的模板std::enable_if 来执行此操作。之后,字符串的行为就像在 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
                

                【讨论】:

                  【解决方案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);
                  

                  【讨论】:

                    【解决方案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++ 中是否会有更好的东西?

                    【讨论】:

                      【解决方案20】:

                      在 c11 中:

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

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

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

                      将打印:消息编号:10

                      【讨论】:

                        【解决方案21】:

                        您还可以“扩展”字符串类并选择您喜欢的运算符(

                        这里是使用操作符

                        注意:如果您取消注释 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;
                        }
                        

                        【讨论】:

                          【解决方案22】:

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

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

                          然后

                          auto str = make_string("hello" << " there" << 10 << '$');
                          

                          【讨论】:

                            【解决方案23】:

                            这对我有用:

                            #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
                            

                            【讨论】:

                            • 每当有人将宏用于比代码保护或常量更复杂的事情时,小猫都会哭泣:P
                            • 除了不开心的小猫:为每个参数创建一个字符串对象,这是不必要的。
                            • 投反对票,因为使用宏绝对是一个糟糕的解决方案
                            • 即使对于 C,这也会让我感到恐惧,但在 C++ 中,它是恶魔般的。 @RuiMarques:在哪些情况下,宏对于常量来说比 const 或(如果需要零存储)enum 更好?
                            • @underscore_d 有趣的问题,但我没有答案。也许答案是否定的。
                            【解决方案24】:

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

                            #include <iostream.h> // for string
                            
                            string myName = "";
                            int _age = 30;
                            myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;
                            

                            【讨论】:

                            • 我使用 C++ borland builder 6,它对我来说很好用。不要忘记包含此标题#include &lt;iostream.h&gt; // string#include &lt;system.hpp&gt; // ansiString
                            • += 在这种情况下没有重载,似乎认为您添加了数字而不是连接字符串
                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2011-11-06
                            • 1970-01-01
                            • 2012-02-02
                            • 2016-10-19
                            • 2015-09-18
                            • 2014-02-18
                            • 1970-01-01
                            相关资源
                            最近更新 更多