Bagaimana cara menggabungkan beberapa string C ++ pada satu baris?

150

C # memiliki fitur sintaksis di mana Anda dapat menggabungkan banyak tipe data secara bersamaan dalam 1 baris.

string s = new String();
s += "Hello world, " + myInt + niceToSeeYouString;
s += someChar1 + interestingDecimal + someChar2;

Apa yang akan menjadi setara dalam C ++? Sejauh yang saya bisa lihat, Anda harus melakukan semuanya pada baris terpisah karena tidak mendukung banyak string / variabel dengan operator +. Ini baik-baik saja, tetapi tidak terlihat rapi.

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

Kode di atas menghasilkan kesalahan.

Nick Bolton
sumber
4
Seperti yang dijelaskan di tempat lain, ini bukan karena "itu tidak mendukung banyak string / variabel dengan operator +" - tetapi karena Anda mencoba untuk menambahkan char *pointer satu sama lain. Itulah yang menghasilkan kesalahan - karena menjumlahkan pointer adalah tidak masuk akal. Seperti disebutkan di bawah, buat setidaknya operan ke-1 menjadi std::string, dan tidak ada kesalahan sama sekali.
underscore_d
Kesalahan mana yang dihasilkan?
Wolf
Kemungkinan rangkap dari Cara menggabungkan string std :: dan int?
vitaut

Jawaban:

240
#include <sstream>
#include <string>

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

Lihatlah artikel Guru Minggu Ini dari Herb Sutter: The String Formatters of Manor Farm

Paolo Tedesco
sumber
6
Coba ini:std::string s = static_cast<std::ostringstream&>(std::ostringstream().seekp(0) << "HelloWorld" << myInt << niceToSeeYouString).str();
Byzantian
42
ss << "Wow, rangkaian string dalam C ++ mengesankan" << "atau tidak."
joaerl
4
Hanya untuk menyebutkan cara lain: menggunakan beberapa append: string s = string ("abc"). Append ("def"). Append (otherStrVar). Append (to_string (123));
Patricio Rossi
1
std::stringstream ss; ss << "Hello, world, " << myInt << niceToSeeYouString; std::string s = ss.str();cukup banyak satu baris
Kotauskas
74

Dalam 5 tahun tidak ada yang menyebutkan .append?

#include <string>

std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");
Michel
sumber
Karena rumit jika dibandingkan dengan hanya menambahkan teks dalam satu baris.
Hi-Angel
11
s.append("One"); s.append(" line");
Jonny
16
@ Jonny s.append("One").append(" expression");Mungkin saya harus mengedit yang asli untuk menggunakan nilai balik dengan cara ini?
Eponim
5
@ SilverMöls OP menyatakan spada baris yang berbeda dalam kode C # yang sama dan dalam kode C ++ yang tidak dikompilasi. C ++ s += "Hello world, " + "nice to see you, " + "or not.";yang diinginkannya adalah yang dapat dituliss.append("Hello world, ").append("nice to see you, ").append("or not.");
Eponim
4
Keuntungan utama appendadalah bahwa ia juga berfungsi ketika string berisi karakter NUL.
John S.
62
s += "Hello world, " + "nice to see you, " + "or not.";

Literal array karakter tersebut bukan C ++ std :: string - Anda perlu mengonversinya:

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

Untuk mengonversi ints (atau jenis streamable lainnya), Anda dapat menggunakan boost lexical_cast atau menyediakan fungsi Anda sendiri:

template <typename T>
string Str( const T & t ) {
   ostringstream os;
   os << t;
   return os.str();
}

Anda sekarang dapat mengatakan hal-hal seperti:

string s = string("The meaning is ") + Str( 42 );
Komunitas
sumber
16
Anda hanya perlu mengonversi yang pertama secara eksplisit: s + = string ("Hello world,") + "senang melihat Anda," + "atau tidak.";
Ferruccio
8
Ya, tapi saya tidak bisa menjelaskan mengapa!
1
meningkatkan :: lexical_cast - bagus dan mirip pada fungsi Str Anda :)
Bayda
2
Rangkaian yang dilakukan di sebelah kanan konstruktor string("Hello world")dilakukan melalui operator+()didefinisikan di kelas string. Jika tidak ada stringobjek dalam ekspresi, rangkuman menjadi jumlah ar pointer char*.
davide
41

Kode Anda dapat ditulis sebagai 1 ,

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

... tapi saya ragu itu yang Anda cari. Dalam kasus Anda, Anda mungkin mencari aliran:

std::stringstream ss;
ss << "Hello world, " << 42 << "nice to see you.";
std::string s = ss.str();

1 " dapat ditulis sebagai ": Ini hanya berfungsi untuk string literal. Penggabungan dilakukan oleh kompiler.

John Dibling
sumber
11
Contoh pertama Anda layak disebutkan, tetapi tolong sebutkan juga bahwa itu hanya berfungsi untuk string literal "penyambung" (kompiler melakukan penggabungan itu sendiri).
j_random_hacker
Contoh pertama memicu kesalahan bagi saya jika string sebelumnya dinyatakan sebagai misalnya const char smthg[] = "smthg": / Apakah ini bug?
Hi-Angel
@ Hai-Malaikat Sayangnya tidak, sebagai gantinya, Anda dapat #definemenggunakan senar Anda untuk menyiasatinya, meskipun ini membawa masalah sendiri.
cz
27

Menggunakan C ++ 14 literal yang ditentukan pengguna dan std::to_stringkode menjadi lebih mudah.

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;

Perhatikan bahwa penggabungan string literal dapat dilakukan pada waktu kompilasi. Hapus saja +.

str += "Hello World, " "nice to see you, " "or not";
Rapptz
sumber
2
Sejak C ++ 11 Anda dapat menggunakan std :: to_string
Patricio Rossi
literal yang ditentukan pengguna juga sejak C ++ 11 <> . Saya diedit.
Stack Danny
@StackDanny Perubahannya salah. Ketika saya mengatakan "C ++ 14" saya merujuk pada std::literals::string_literals, bukan konsep UDL.
Rapptz
16

Untuk menawarkan solusi yang lebih one-line-ish: Sebuah fungsi concatdapat diimplementasikan untuk mengurangi solusi berbasis stringstream "klasik" menjadi satu pernyataan . Ini didasarkan pada template variadic dan penerusan yang sempurna.


Pemakaian:

std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);

Penerapan:

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();
}
SebastianK
sumber
bukankah ini akan menjadi waktu kompilasi mengasapi jika ada beberapa kombinasi yang berbeda dalam basis kode besar.
Shital Shah
1
@ShitalShah tidak lebih dari sekedar menulis hal-hal yang sebaris secara manual, karena fungsi pembantu ini hanya akan mendapatkan inline pula.
underscore_d
13

Di C ++ 20 Anda dapat melakukan:

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

Sampai saat itu Anda dapat melakukan hal yang sama dengan perpustakaan {fmt} :

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

Penafian : Saya penulis {fmt}.

riwayat hidup
sumber
7

boost :: format

atau std :: stringstream

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

Masalah sebenarnya adalah bahwa menggabungkan string literal dengan +gagal dalam C ++:

string s;
s += "Hello world, " + "nice to see you, " + "or not.";
Kode di atas menghasilkan kesalahan.

Dalam C ++ (juga dalam C), Anda menggabungkan string literal dengan menempatkannya tepat di sebelah satu sama lain:

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

Ini masuk akal, jika Anda menghasilkan kode di makro:

#define TRACE(arg) cout << #arg ":" << (arg) << endl;

... makro sederhana yang dapat digunakan seperti ini

int a = 5;
TRACE(a)
a += 7;
TRACE(a)
TRACE(a+7)
TRACE(17*11)

( demo langsung ... )

atau, jika Anda bersikeras untuk menggunakan +string literal (seperti yang disarankan oleh underscore_d ):

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

Solusi lain menggabungkan string dan a const char*untuk setiap langkah penggabungan

string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";
Serigala
sumber
Saya juga menggunakan teknik ini banyak, tetapi bagaimana jika satu atau lebih variabel int / string? .eg string s = "abc" "def" (int) y "ghi" (std :: string) z "1234"; kemudian, sprintf masih merupakan solusi terbaik dari yang lebih buruk.
Bart Mensfort
@ BartMensfort tentu saja sprintfmerupakan pilihan, tetapi ada juga std :: stringstream yang mencegah masalah dengan buffer yang berukuran terlalu kecil.
Wolf
5
auto s = string("one").append("two").append("three")
Shital Shah
sumber
3

Anda harus mendefinisikan operator + () untuk setiap tipe data yang ingin Anda hubungkan ke string, namun karena operator << ditentukan untuk sebagian besar tipe, Anda harus menggunakan std :: stringstream.

Sial, kalahkan 50 detik ...

Tstenner
sumber
1
Anda sebenarnya tidak bisa mendefinisikan operator baru pada tipe bawaan seperti char dan int.
Tyler McHenry
1
@TylerMcHenry Bukannya saya akan merekomendasikan hal ini dalam kasus ini, tetapi Anda pasti bisa:std::string operator+(std::string s, int i){ return s+std::to_string(i); }
Eponim
3

Jika Anda menuliskannya +=, tampilannya hampir sama dengan C #

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

Seperti yang dikatakan orang lain, masalah utama dengan kode OP adalah bahwa operator +tidak terhubung const char *; itu bekerja dengan baik std::string.

Berikut solusi lain yang menggunakan C ++ 11 lambdas dan for_eachdan memungkinkan untuk menyediakan separatoruntuk memisahkan string:

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

Pemakaian:

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

Tampaknya skala dengan baik (linear), setidaknya setelah tes cepat di komputer saya; inilah tes cepat yang saya tulis:

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

Hasil (milidetik):

join(10)   : 2
join(100)  : 10
join(1000) : 91
join(10000): 898
elnigno
sumber
3

Mungkin Anda menyukai solusi "Streamer" saya untuk benar-benar melakukannya dalam satu baris:

#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;
}
bterwijn
sumber
2

Inilah solusi satu-liner:

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

Meskipun agak jelek, saya pikir ini sebersih kucing Anda masuk C ++.

Kami melemparkan argumen pertama ke a std::stringdan kemudian menggunakan urutan evaluasi (kiri ke kanan) operator+untuk memastikan bahwa operan kirinya selalu a std::string. Dengan cara ini, kami menggabungkan tombol std::stringdi sebelah kiri dengan const char *operan di sebelah kanan dan mengembalikan yang lain std::string, memberikan efek.

Catatan: ada beberapa pilihan untuk operan kanan, termasuk const char *, std::string, dan char.

Terserah Anda untuk memutuskan apakah angka ajaib adalah 13 atau 6227020800.

Apollys mendukung Monica
sumber
Ah, kamu lupa, @Apollys, angka ajaib universal adalah 42.: D
Mr.Zeus
1

Anda dapat menggunakan tajuk ini untuk hal ini: https://github.com/theypsilon/concat

using namespace concat;

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

Di bawah tenda Anda akan menggunakan std :: ostringstream.

José Manuel
sumber
1

Jika Anda bersedia menggunakan, c++11Anda dapat menggunakan string literal yang ditentukan pengguna dan menetapkan dua templat fungsi yang membebani operator plus untuk suatu std::stringobjek dan objek lainnya. Satu-satunya perangkap adalah bukan untuk membebani operator plus std::string, jika tidak kompiler tidak tahu operator mana yang harus digunakan. Anda dapat melakukan ini dengan menggunakan templat std::enable_ifdari type_traits. Setelah itu string berperilaku seperti di Java atau C #. Lihat contoh implementasi saya untuk detailnya.

Kode utama

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

File c_sharp_strings.hpp

Sertakan file header ini di semua tempat di mana Anda ingin memiliki string ini.

#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
Scindix
sumber
1

Sesuatu seperti ini bekerja untuk saya

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);
smoothware
sumber
1

Berdasarkan solusi di atas saya membuat kelas var_string untuk proyek saya untuk membuat hidup lebih mudah. Contoh:

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

Kelas itu sendiri:

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

Masih bertanya-tanya apakah akan ada sesuatu yang lebih baik di C ++?

Bart Mensfort
sumber
1

Di c11:

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

ini memungkinkan Anda untuk membuat panggilan fungsi seperti ini:

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

akan mencetak: nomor pesan: 10

devcodexyz
sumber
0

Anda juga dapat "memperluas" kelas string dan memilih operator yang Anda sukai (<<, &, |, dll ...)

Berikut adalah kode menggunakan operator << untuk menunjukkan tidak ada konflik dengan stream

catatan: jika Anda membatalkan komentar s1.reserve (30), hanya ada 3 permintaan operator baru () (1 untuk s1, 1 untuk s2, 1 untuk cadangan; sayangnya Anda tidak dapat memesan pada waktu konstruktor); tanpa cadangan, s1 harus meminta lebih banyak memori saat ia tumbuh, jadi itu tergantung pada faktor pertumbuhan implementasi kompiler Anda (milik saya tampaknya 1,5, 5 baru () memanggil dalam contoh ini)

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;
}
ddy
sumber
0

Stringstream dengan makro preprokrosesor sederhana menggunakan fungsi lambda tampak bagus:

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

lalu

auto str = make_string("hello" << " there" << 10 << '$');
asikorski
sumber
-1

Ini bekerja untuk saya:

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

Keluaran:

c:\example\myfile.txt
erik80
sumber
12
Seekor anak kucing menangis setiap kali seseorang menggunakan makro untuk sesuatu yang lebih kompleks daripada penjaga kode atau konstanta: P
Rui Marques
1
Di samping anak kucing yang tidak bahagia: Untuk setiap argumen, objek string dibuat yang tidak perlu.
SebastianK
2
downvoted, karena menggunakan makro jelas merupakan solusi yang buruk
dhaumann
ini akan membuat saya tersentak ngeri bahkan untuk C, tetapi dalam C ++, itu jahat. @RuiMarques: dalam situasi apa makro lebih baik untuk konstanta daripada constatau (jika penyimpanan nol adalah persyaratan) sebuah enumakan menjadi?
underscore_d
@underscore_d pertanyaan menarik tapi saya tidak punya jawaban untuk itu. Mungkin jawabannya tidak.
Rui Marques
-1

Sudahkah Anda mencoba menghindari + =? alih-alih gunakan var = var + ... itu berhasil untuk saya.

#include <iostream.h> // for string

string myName = "";
int _age = 30;
myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;
vincent thorpe
sumber
Saya menggunakan C ++ borland builder 6, dan berfungsi dengan baik untuk saya. jangan lupa untuk menyertakan tajuk ini#include <iostream.h> // string #include <system.hpp> // ansiString
vincent thorpe
+ = tidak kelebihan beban untuk kasus ini, tampaknya berpikir Anda telah menambahkan angka dan tidak merangkai string
vincent thorpe