Come posso concatenare più stringhe C ++ su una riga?


150

C # ha una funzione di sintassi in cui è possibile concatenare molti tipi di dati su una riga.

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

Quale sarebbe l'equivalente in C ++? Per quanto posso vedere, dovresti fare tutto su righe separate in quanto non supporta più stringhe / variabili con l'operatore +. Va bene, ma non sembra pulito.

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

Il codice sopra riportato produce un errore.


4
Come spiegato altrove, ciò non è dovuto al fatto che "non supporta più stringhe / variabili con l'operatore +", ma piuttosto perché si sta tentando di aggiungere char *puntatori l'uno all'altro. Questo è ciò che genera l'errore, perché i puntatori di sommatoria sono privi di senso. Come indicato di seguito, trasforma almeno il 1 ° operando in un std::stringe non c'è alcun errore.
underscore_d

Quale errore è stato prodotto?
Lupo,

Risposte:


240
#include <sstream>
#include <string>

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

Dai un'occhiata a questo articolo del Guru della settimana da Herb Sutter: The String Formatters of Manor Farm


6
Prova questo:std::string s = static_cast<std::ostringstream&>(std::ostringstream().seekp(0) << "HelloWorld" << myInt << niceToSeeYouString).str();
Byzantian

42
ss << "Caspita, la concatenazione di stringhe in C ++ è impressionante" << "o no."
joaerl,

4
Solo per nominare un altro modo: usando append multipli: 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();è praticamente a una riga
Kotauskas il

74

In 5 anni nessuno ha menzionato .append?

#include <string>

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

Perché è ingombrante rispetto al solo aggiungere un testo in una riga.
Ciao Angelo

11
s.append("One"); s.append(" line");
Jonny,

16
@Jonny s.append("One").append(" expression");Forse dovrei modificare l'originale per usare il valore di ritorno in questo modo?
Eponimo

5
@ SilverMöls L'OP dichiara ssu una riga diversa nel codice C # equivalente e nel suo codice C ++ non compilante. Il suo C ++ desiderato è quello s += "Hello world, " + "nice to see you, " + "or not.";che può essere scrittos.append("Hello world, ").append("nice to see you, ").append("or not.");
Eponimo

4
Un grande vantaggio di appendè che funziona anche quando le stringhe contengono caratteri NUL.
John S.

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

Questi letterali array di caratteri non sono C ++ std :: stringhe - è necessario convertirli:

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

Per convertire ints (o qualsiasi altro tipo streaming) è possibile utilizzare un boost lexical_cast o fornire la propria funzione:

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

Ora puoi dire cose come:

string s = string("The meaning is ") + Str( 42 );

16
Devi solo convertire esplicitamente il primo: s + = string ("Hello world,") + "piacere di vederti," + "o no.";
Ferruccio,

8
Sì, ma non ho potuto affrontare spiegando perché!

1
boost :: lexical_cast - bello e simile sulla tua funzione Str :)
bayda

2
Le concatenazioni eseguite a destra del costruttore string("Hello world")vengono eseguite tramite operator+()definite nella classe string. Se non c'è alcun stringoggetto nell'espressione, la concatenazione diventa una semplice somma di puntatori a caratteri char*.
davide

41

Il tuo codice può essere scritto come 1 ,

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

... ma dubito che sia quello che stai cercando. Nel tuo caso, probabilmente stai cercando flussi:

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

1 " può essere scritto come ": funziona solo per i letterali stringa. La concatenazione viene eseguita dal compilatore.


11
Vale la pena menzionare il tuo primo esempio, ma ricorda anche che funziona solo per "concatenare" stringhe letterali (il compilatore esegue la concatenazione stessa).
j_random_hacker

Il primo esempio ha innescato un errore per me se una stringa era stata precedentemente dichiarata come ad esempio const char smthg[] = "smthg": / È un bug?
Ciao Angelo

@ Hi-Angel Sfortunatamente no, invece, puoi fare in modo che la #definetua stringa aggiri questo, anche se questo porta i suoi problemi.
CZ

27

L'utilizzo di valori letterali definiti dall'utente in C ++ 14 e std::to_stringil codice diventa più semplice.

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;

Si noti che i concatenanti letterali di stringhe possono essere eseguiti in fase di compilazione. Basta rimuovere il +.

str += "Hello World, " "nice to see you, " "or not";

2
Da C ++ 11 puoi usare std :: to_string
Patricio Rossi il

valori letterali definiti dall'utente anche dal C ++ 11 <> . Ho modificato.
Stack Danny

@StackDanny Il cambiamento è sbagliato. Quando dico "C ++ 14" mi riferisco al std::literals::string_literals, non al concetto di UDL.
Rapptz,

16

Per offrire una soluzione che è più di una riga: è concatpossibile implementare una funzione per ridurre la soluzione "classica" basata su stringhe a una singola istruzione . Si basa su modelli variadici e inoltro perfetto.


Uso:

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

Implementazione:

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

questo non diventerebbe un tempo di compilazione gonfiabile se ci sono diverse combinazioni di codice nella grande base di codice.
Shital Shah,

1
@ShitalShah non è altro che scrivere semplicemente queste cose in linea manualmente, poiché queste funzioni di supporto verranno comunque integrate.
underscore_d

13

In C ++ 20 sarai in grado di fare:

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

Fino ad allora potresti fare lo stesso con la libreria {fmt} :

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

Disclaimer : sono l'autore di {fmt}.


7

boost :: formato

o std :: stringstream

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

6

Il vero problema era che la concatenazione dei letterali di stringa con +errori in C ++:

string s;
s += "Hello world, " + "nice to see you, " + "or not.";
Il codice sopra riportato produce un errore.

In C ++ (anche in C), concatenate i letterali delle stringhe semplicemente posizionandoli uno accanto all'altro:

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

Questo ha senso, se generi codice nelle macro:

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

... una semplice macro che può essere utilizzata in questo modo

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

( demo live ... )

oppure, se insisti nell'usare i +letterali for string (come già suggerito da underscore_d ):

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

Un'altra soluzione combina una stringa e una const char*per ogni passaggio di concatenazione

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

Uso anche questa tecnica molto, ma cosa succede se una o più variabili sono int / string? .eg string s = "abc" "def" (int) y "ghi" (std :: string) z "1234"; quindi, sprintf è ancora la migliore delle soluzioni peggiori.
Bart Mensfort,

@BartMensfort ovviamente sprintfè un'opzione, ma c'è anche std :: stringstream che impedisce problemi con buffer di dimensioni inferiori.
Lupo,


3

Dovresti definire l'operatore + () per ogni tipo di dati che desideri conciliare alla stringa, ma poiché l'operatore << è definito per la maggior parte dei tipi, dovresti usare std :: stringstream.

Dannazione, batti di 50 secondi ...


1
In realtà non è possibile definire nuovi operatori su tipi predefiniti come char e int.
Tyler McHenry,

1
@TylerMcHenry Non che lo consiglierei in questo caso, ma sicuramente puoi:std::string operator+(std::string s, int i){ return s+std::to_string(i); }
Eponimo

3

Se scrivi il +=, sembra quasi uguale a C #

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

3

Come altri hanno affermato, il problema principale con il codice OP è che l'operatore +non si concatena const char *; funziona con std::string, però.

Ecco un'altra soluzione che utilizza lambda C ++ 11 e for_eachche consente di fornire un separatorper separare le stringhe:

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

Uso:

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

Sembra ridimensionare bene (linearmente), almeno dopo un rapido test sul mio computer; ecco un breve test che ho scritto:

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

Risultati (millisecondi):

join(10)   : 2
join(100)  : 10
join(1000) : 91
join(10000): 898

3

Forse ti piace la mia soluzione "Streamer" per farlo davvero in una riga:

#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

Ecco la soluzione one-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;
}

Anche se è un po 'brutto, penso che sia pulito come il tuo gatto entra in C ++.

Stiamo lanciando il primo argomento in a std::stringe quindi usando l'ordine di valutazione (da sinistra a destra) operator+per assicurarci che il suo operando di sinistra sia sempre a std::string. In questo modo, concateniamo la std::stringsinistra con l' const char *operando a destra e ne restituiamo un'altra std::string, a cascata l'effetto.

Nota: ci sono alcune opzioni per l'operando di destra, tra cui const char *, std::string, e char.

Sta a te decidere se il numero magico è 13 o 6227020800.


Ah, dimentica, @Apollys, il numero magico universale è 42.: D
Mr.Zeus


1

Se si desidera utilizzare c++11è possibile utilizzare valori letterali stringa definiti dall'utente e definire due modelli di funzione che sovraccaricano l'operatore più per un std::stringoggetto e qualsiasi altro oggetto. L'unico inconveniente è non sovraccaricare gli operatori plus di std::string, altrimenti il ​​compilatore non sa quale operatore utilizzare. Puoi farlo usando il modello std::enable_ifda type_traits. Successivamente le stringhe si comportano proprio come in Java o C #. Vedi la mia implementazione di esempio per i dettagli.

Codice principale

#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

Includi questo file di intestazione in tutti i punti in cui desideri disporre di queste stringhe.

#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

Qualcosa del genere funziona per me

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

Sulla base delle soluzioni di cui sopra, ho creato una var_string di classe per il mio progetto per semplificare la vita. Esempi:

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

La classe stessa:

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

Ti stai ancora chiedendo se ci sarà qualcosa di meglio in C ++?


1

In c11:

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

questo ti consente di creare una chiamata di funzione in questo modo:

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

stamperà: numero messaggio: 10


0

puoi anche "estendere" la classe di stringhe e scegliere l'operatore che preferisci (<<, &, |, ecc ...)

Ecco il codice che utilizza l'operatore << per mostrare che non vi è alcun conflitto con i flussi

nota: se si decommenta s1.reserve (30), ci sono solo 3 nuove richieste dell'operatore () (1 per s1, 1 per s2, 1 per riserva; purtroppo non è possibile prenotare al momento del costruttore); senza riserve, s1 deve richiedere più memoria man mano che cresce, quindi dipende dal fattore di crescita dell'implementazione del compilatore (il mio sembra essere 1,5, 5 chiamate new () in questo esempio)

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 con una semplice macro preproccessor che utilizza una funzione lambda sembra carino:

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

e poi

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

-1

Questo funziona per me:

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

Produzione:

c:\example\myfile.txt

12
Un gattino piange ogni volta che qualcuno usa le macro per qualcosa di più complesso delle guardie del codice o delle costanti: P
Rui Marques,

1
Accanto a gattini infelici: per ogni argomento viene creato un oggetto stringa che non è necessario.
SebastianK,

2
ridimensionato, dal momento che l'utilizzo di macro è sicuramente una cattiva soluzione
dhaumann,

questo mi farebbe sussultare nell'orrore anche per C, ma in C ++ è diabolico. @RuiMarques: in quali situazioni le macro sono migliori per le costanti rispetto a un consto (se lo spazio di archiviazione zero è un requisito) enumsarebbe?
underscore_d

@underscore_d domanda interessante ma non ho una risposta per questo. Forse la risposta è nessuna.
Rui Marques,

-1

Hai provato a evitare il + =? invece usa var = var + ... ha funzionato per me.

#include <iostream.h> // for string

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

Uso C ++ borland builder 6 e funziona bene per me. non dimenticare di includere queste intestazioni#include <iostream.h> // string #include <system.hpp> // ansiString
vincent thorpe,

+ = non è sovraccarico per questo caso, sembra pensare che tu abbia aggiunto numeri e non concatenare la stringa
vincent thorpe
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.