複数のC ++文字列を1行に連結するにはどうすればよいですか?


150

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

上記のコードはエラーを生成します。


4
他の場所で説明したように、これは「+演算子で複数の文字列/変数をサポートしていない」ためではなくchar *、相互にポインタを追加しようとしているためです。これがエラーの原因です-ポインタの合計は無意味です。以下に示すように、少なくとも第1オペランドをにするとstd::string、エラーはまったく発生しません。
underscore_d 2015

どのエラーが発生しましたか?
ウルフ

回答:


239
#include <sstream>
#include <string>

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

Herb Sutter:Manor Farmの文字列フォーマッターからのこのGuru Of The Weekの記事をご覧ください


6
これを試してください:std::string s = static_cast<std::ostringstream&>(std::ostringstream().seekp(0) << "HelloWorld" << myInt << niceToSeeYouString).str();
ビザンチアン

41
ss << "うわー、C ++での文字列連結は印象的です" << "またはそうではない。"
joaerl 2014年

4
別の言い方をすれば、複数の追加を使用します:string s = string( "abc")。append( "def")。append(otherStrVar).append(to_string(123));
パトリシオロッシ

1
std::stringstream ss; ss << "Hello, world, " << myInt << niceToSeeYouString; std::string s = ss.str();ほぼ1行です
コタウカス

74

5年間で誰も言及してい.appendませんか?

#include <string>

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

なぜなら、1行にテキストを追加するだけの場合と比較すると、面倒だからです。
Hi-Angel、

11
s.append("One"); s.append(" line");
ジョニー2015

16
@ジョニーs.append("One").append(" expression");多分私はこのように戻り値を使用するようにオリジナルを編集する必要がありますか?
2015

5
@SilverMölsOP sは、同等のC#コードとコンパイルされていないC ++コードの別の行で宣言します。彼の希望するC ++はs += "Hello world, " + "nice to see you, " + "or not.";、記述可能なものですs.append("Hello world, ").append("nice to see you, ").append("or not.");
Eponymous

4
の主な利点はappend、文字列にNUL文字が含まれている場合にも機能することです。
ジョン

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

これらの文字配列リテラルはC ++ std :: stringsではありません-それらを変換する必要があります:

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

int(またはその他のストリーミング可能な型)を変換するには、ブースト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 );

16
最初のものだけを明示的に変換する必要があるだけです。
Ferruccio

8
ええ、でも私は理由を説明することができませんでした!

1
boost :: lexical_cast-Str関数についても同様です。)
bayda

2
コンストラクタの右側で行われる連結は、クラスで定義されたものをstring("Hello world")介して実行operator+()されstringます。string式にオブジェクトがない場合、連結は単なるcharポインタの合計になりますchar*
デヴィッド

41

コードは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 " " として記述できます:これは文字列リテラルに対してのみ機能します。連結はコンパイラーによって行われます。


11
最初の例は言及する価値がありますが、「連結」リテラル文字列に対してのみ機能することも述べてください(コンパイラー自体が連結を実行します)。
j_random_hacker

最初の例では、文字列が以前にたとえばconst char smthg[] = "smthg":/ として宣言されていた場合、エラーが発生しました。
Hi-Angel

@ Hi-Angel残念ながら#define、文字列でこれを回避することはできますが、これには固有の問題があります。
CZ

27

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

2
C ++ 11以降はstd :: to_stringを使用できます
Patricio Rossi

C ++ 11 <>以降、ユーザー定義のリテラル。編集しました。
スタックダニー

@StackDanny変更が間違っています。「C ++ 14」と言うときstd::literals::string_literalsは、UDLの概念ではなくを指します。
Rapptz

16

より1行っぽいソリューションを提供するには:関数concatを実装して、「クラシック」な文字列ベースのソリューションを1 つのステートメントに減らすことができます。可変テンプレートと完全転送に基づいています。


使用法:

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

大きなコードベースにいくつかの異なる組み合わせがある場合、これはコンパイル時の肥大化にならないでしょう。
Shital Shah 2017

1
@ShitalShahは、これらのヘルパー関数がとにかくインライン化されるため、そのようなものを手動でインラインで記述するだけではありません。
underscore_d

13

C ++ 20では、次のことができるようになります。

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

それまでは、{fmt}ライブラリを使用して同じことを行うことができます。

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

免責事項:私は{fmt}の作成者です。


7

boost :: format

またはstd :: stringstream

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

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)

ライブデモ...

または、+underscore_dですでに提案されているように)文字列リテラルのforの使用を主張する場合:

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

私もこの手法をよく使用していますが、1つ以上の変数がint / stringの場合はどうなりますか?.eg string s = "abc" "def"(int)y "ghi"(std :: string)z "1234"; その場合、sprintfは依然として最悪の解決策のベストです。
Bart Mensfort 2017年

もちろん@BartMensfortはsprintfオプションですが、標準サイズのバッファの問題を防ぐstd :: stringstreamもあります。
Wolfの


3

文字列に連結するすべてのデータ型に対してoperator +()を定義する必要がありますが、operator <<はほとんどの型に対して定義されているため、std :: stringstreamを使用する必要があります。

くそー50秒で...


1
実際には、charやintなどの組み込み型に新しい演算子を定義することはできません。
タイラーマクヘンリー

1
@TylerMcHenryこの場合、私はそれをお勧めしますが、確かに次のことはできます:std::string operator+(std::string s, int i){ return s+std::to_string(i); }
代名詞

3

を書き出す+=と、C#とほとんど同じように見えます

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

3

他の人が言ったように、OPコードの主な問題は、演算子+が連結しないことconst char *です。でも動作しstd::stringます。

C ++ 11ラムダ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

3

多分あなたは私の「ストリーマー」ソリューションが本当に一行でそれをするのが好きです:

#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;
}

2

ワンライナーソリューションは次のとおりです。

#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次に(左から右へ)評価順序operator+を使用して、左のオペランドが常にaであることを確認しstd::stringます。このようにstd::stringして、左側のをconst char *右側のオペランドと連結し、別のを返しますstd::string、効果をカスケードします。

注:を含む右側のオペランドのためのいくつかのオプションが存在するconst char *std::stringchar

マジックナンバーを13にするか6227020800にするかは、あなた次第です。


D:ああ、あなたは忘れて、@Apollysは、ユニバーサルマジックナンバーは42です
Mr.Zeusは、


1

使用するc++11場合は、ユーザー定義の文字列リテラルを利用して、std::stringオブジェクトと他のオブジェクトのプラス演算子をオーバーロードする2つの関数テンプレートを定義できます。唯一の落とし穴は、のプラス演算子をオーバーロード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

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

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 ++でもっと良いものがあるかどうかまだ疑問に思っていますか?


1

c11の場合:

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

これにより、次のような関数呼び出しを作成できます。

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

印刷されます:メッセージ番号:10


0

文字列クラスを「拡張」して、希望する演算子を選択することもできます(<<、&、|など)。

以下は、<<演算子を使用して、ストリームとの競合がないことを示すコードです。

注:s1.reserve(30)のコメントを解除すると、3つのnew()オペレーター要求しかありません(s1に1つ、s2に1つ、reserveに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;
}

0

ラムダ関数を使用した単純な前処理マクロを備えたStringstreamはいいようです:

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

その後

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

-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

12
コードガードや定数よりも複雑なものに誰かがマクロを使用するたびに、子猫は泣きます:P
Rui Marques

1
不幸な子猫の横:各引数に対して、不要な文字列オブジェクトが作成されます。
SebastianK

2
マクロの使用は
明らかに

これはCでさえ恐怖に陥るでしょうが、C ++ではそれは悪魔的です。@RuiMarques:どの状況で、定数のマクロはa constまたは(ゼロストレージが必要な場合)よりも優れていenumますか?
underscore_d 2015

@underscore_d興味深い質問ですが、答えはありません。おそらく答えはありません。
Rui Marques

-1

+ =を避けようとしましたか?代わりにvar = var + ...を使用してください。

#include <iostream.h> // for string

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

私はC ++ボーランドビルダー6を使用していますが、問題なく動作します。このヘッダーを含めることを忘れないでください#include <iostream.h> // string #include <system.hpp> // ansiString
ビンセントソープ

+ =この場合、オーバーロードされていません。数字を追加して文字列を連結していないと考えているようです
vincent thorpe
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.