2013-03-11 10 views
8

Mi piacerebbe costruire un std::string da un std::vector<std::string>.Come costruire una stringa: std :: vector <string>?

potrei usare std::stringsteam, ma immaginare che ci sia un modo più breve:

std::string string_from_vector(const std::vector<std::string> &pieces) { 
    std::stringstream ss; 

    for(std::vector<std::string>::const_iterator itr = pieces.begin(); 
     itr != pieces.end(); 
     ++itr) { 
    ss << *itr; 
    } 

    return ss.str(); 
} 

Altrimenti come potrei fare questo?

+1

Forse 'res std :: string; per (...) {res + = * it; } '? –

risposta

25

È possibile utilizzare la funzione standard std::accumulate() dalla testata <numeric> (che funziona perché un sovraccarico di operator + è definito per string s che restituisce la concatenazione dei suoi due argomenti):

#include <vector> 
#include <string> 
#include <numeric> 
#include <iostream> 

int main() 
{ 
    std::vector<std::string> v{"Hello, ", " Cruel ", "World!"}; 
    std::string s; 
    s = accumulate(begin(v), end(v), s); 
    std::cout << s; // Will print "Hello, Cruel World!" 
} 

In alternativa, è possibile utilizzare una più efficiente, piccolo for ciclo:

#include <vector> 
#include <string> 
#include <iostream> 

int main() 
{ 
    std::vector<std::string> v{"Hello, ", "Cruel ", "World!"}; 
    std::string result; 
    for (auto const& s : v) { result += s; } 
    std::cout << result; // Will print "Hello, Cruel World!" 
} 
+0

Mi picchiato lol. – bstamour

+0

@bstamour: Sì, fondamentalmente entrambe le risposte sono corrette :) –

+6

Carino, ma incurante. Alloca una nuova stringa per ogni singola operazione, perché usa 'operator +' e genera una nuova stringa, invece di 'operator + =' per modificare uno esistente. –

7

perché non utilizzare operatore + per aggiungere insieme?

std::string string_from_vector(const std::vector<std::string> &pieces) { 
    return std::accumulate(pieces.begin(), pieces.end(), std::string("")); 
} 

std :: accumulano utilizza std :: più sotto il cofano di default, e l'aggiunta di due stringhe è la concatenazione in C++, in quanto l'operatore + è sovraccarico per std :: string.

+0

Direi la funzione come 'to_string' invece di' string_from_vector'. – Nawaz

+2

Probabilmente lo farei anch'io, ma questo è il nome che è stato usato nella domanda originale. – bstamour

0

in C++ 11 il stringstream modo non è troppo spaventoso:

#include <vector> 
#include <string> 
#include <algorithm> 
#include <sstream> 
#include <iostream> 

int main() 
{ 
    std::vector<std::string> v{"Hello, ", " Cruel ", "World!"}; 
    std::stringstream s; 
    std::for_each(begin(v), end(v), [&s](const std::string &elem) { s << elem; }); 
    std::cout << s.str(); 
} 
+1

Perché non usare 'for (auto & i: v) {s << i; } 'invece della linea for_each? –

+2

Perché persino usare un stringstream? Costruisci 's' una stringa e crea il lambda' {s + = elem} ' – Oktalist

34

Questo è così facile che mi stupisce quante persone intelligenti hanno avvitato in su.

C++ 03:

std::string s; 
for (std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) 
    s += *i; 
return s; 

C++ 11: (la MSVC2010 sottoinsieme)

std::string s; 
std::for_each(v.begin(), v.end(), [&](const std::string &piece){ s += piece; }); 
return s; 

C++ 11:

std::string s; 
for (const auto &piece : v) s += piece; 
return s; 

Non utilizzare std::accumulate per la concatenazione di stringhe, è un classico Schlemiel the Painter's algorithm, anche peggio del solito esempio che utilizza strcat in C. Senza la semantica di spostamento C++ 11, incorre in due copie non necessarie dell'accumulatore per ciascun elemento del vettore . Anche con la semantica del movimento, si incorre comunque in una copia non necessaria dell'accumulatore per ogni elemento.

I tre esempi sopra riportati sono O (n).

std::accumulate è O (n^2) per stringhe.


Si potrebbe fare std::accumulate O (n) per le stringhe, fornendo un funtore personalizzato:

std::string s = std::accumulate(v.begin(), v.end(), std::string{}, 
    [](std::string &s, const std::string &piece) -> decltype(auto) { return s += piece; }); 

noti che s deve essere un riferimento al non-const, il tipo di ritorno lambda deve essere un riferimento (quindi decltype(auto)) e il corpo deve utilizzare += non +.

3

La mia scelta personale sarebbe il ciclo basato su intervallo, come in Oktalist's answer.

Boost offre anche una bella soluzione:

#include <boost/algorithm/string/join.hpp> 
#include <iostream> 
#include <vector> 

int main() { 

    std::vector<std::string> v{"first", "second"}; 

    std::string joined = boost::algorithm::join(v, ", "); 

    std::cout << joined << std::endl; 
} 

Questo stampa:

first, second

In ogni caso, trovo l'approccio std::accumulate() un abuso di tale algoritmo (indipendentemente i problemi di complessità).

2

Un po 'tardi alla festa, ma mi è piaciuto il fatto che siamo in grado di utilizzare gli elenchi di inizializzazione:

std::string join(std::initializer_list<std::string> i) 
{ 
    std::vector<std::string> v(i); 
    std::string res; 
    for (const auto &s: v) res += s; 
    return res; 
} 

Poi si può semplicemente richiamare (stile Python):

join({"Hello", "World", "1"}) 
0

Google Abseil ha function absl :: StrJoin che fa quello che ti serve.

Esempio dal file header. Si noti che il separatore può essere anche ""

// std::vector<std::string> v = {"foo", "bar", "baz"}; 
// std::string s = absl::StrJoin(v, "-"); 
// EXPECT_EQ("foo-bar-baz", s);