Como implodir um vetor de strings em uma string (a maneira elegante)

Estou procurando a maneira mais elegante de implodir um vetor de strings em uma string. Abaixo está a solução que estou usando agora:

static std::string& implode(const std::vector& elems, char delim, std::string& s) { for (std::vector::const_iterator ii = elems.begin(); ii != elems.end(); ++ii) { s += (*ii); if ( ii + 1 != elems.end() ) { s += delim; } } return s; } static std::string implode(const std::vector& elems, char delim) { std::string s; return implode(elems, delim, s); } 

Existe algum outro lá fora?

Use boost::algorithm::join(..) :

 #include  ... std::string joinedString = boost::algorithm::join(elems, delim); 

Veja também esta questão .

 std::vector strings; const char* const delim = ", "; std::ostringstream imploded; std::copy(strings.begin(), strings.end(), std::ostream_iterator(imploded, delim)); 

(include , , e )

Se você quer ter um final limpo (sem delimitador final) dê uma olhada aqui

Você deve usar std::ostringstream ao invés de std::string para construir a saída (então você pode chamar seu método str() no final para obter uma string, então sua interface não precisa mudar, apenas os temporários s ).

A partir daí, você poderia mudar para usar std::ostream_iterator , da seguinte forma:

 copy(elems.begin(), elems.end(), ostream_iterator(s, delim)); 

Mas isso tem dois problemas:

  1. delim agora precisa ser um const char* , ao invés de um único char . Nada demais.
  2. std::ostream_iterator escreve o delimitador após cada elemento, incluindo o último. Então você precisa apagar o último no final, ou escrever sua própria versão do iterador que não tem esse aborrecimento. Valeria a pena fazer o último se você tiver um monte de código que precisa de coisas como esta; caso contrário, toda a confusão pode ser evitada (ou seja, use ostringstream mas não ostream_iterator ).

Porque eu amo os one-liners (eles são muito úteis para todos os tipos de coisas estranhas, como você verá no final), aqui está uma solução usando std :: accumulate e C ++ 11 lambda:

 std::accumulate(alist.begin(), alist.end(), std::string(), [](const std::string& a, const std::string& b) -> std::string { return a + (a.length() > 0 ? "," : "") + b; } ) 

Eu acho essa syntax útil com o operador de stream, onde não quero ter todos os tipos de lógica estranha fora do escopo da operação de stream, apenas para fazer uma junit de cadeia simples. Considere, por exemplo, essa declaração de retorno do método que formata uma string usando operadores de stream (usando std;):

 return (dynamic_cast(ostringstream() << "List content: " << endl << std::accumulate(alist.begin(), alist.end(), std::string(), [](const std::string& a, const std::string& b) -> std::string { return a + (a.length() > 0 ? "," : "") + b; } ) << endl << "Maybe some more stuff" << endl )).str(); 
 string join(const vector& vec, const char* delim) { stringstream res; copy(vec.begin(), vec.end(), ostream_iterator(res, delim)); return res.str(); } 

Uma versão que usa std::accumulate :

 #include  #include  #include  struct infix { std::string sep; infix(const std::string& sep) : sep(sep) {} std::string operator()(const std::string& lhs, const std::string& rhs) { std::string rz(lhs); if(!lhs.empty() && !rhs.empty()) rz += sep; rz += rhs; return rz; } }; int main() { std::string a[] = { "Hello", "World", "is", "a", "program" }; std::string sum = std::accumulate(a, a+5, std::string(), infix(", ")); std::cout << sum << "\n"; } 

Aqui está outro que não adiciona o delimitador após o último elemento:

 std::string concat_strings(const std::vector &elements, const std::string &separator) { if (!elements.empty()) { std::stringstream ss; auto it = elements.cbegin(); while (true) { ss << *it++; if (it != elements.cend()) ss << separator; else return ss.str(); } } return ""; 

Especialmente com collections maiores, você quer evitar ter que verificar se ainda está adicionando o primeiro elemento ou não, para garantir que nenhum separador à direita …

Portanto, para a lista vazia ou de elemento único, não há nenhuma iteração.

Intervalos vazios são triviais: return “”.

Elemento único ou multi-elemento pode ser manuseado perfeitamente por accumulate :

 auto join = [](const auto &&range, const auto separator) { if (range.empty()) return std::string(); return std::accumulate( next(begin(range)), // there is at least 1 element, so OK. end(range), range[0], // the initial value [&separator](auto result, const auto &value) { return result + separator + value; }); }; 

Amostra em execução ( requer C ++ 14 ): http://cpp.sh/8uspd

E quanto a solução estúpida simples?

 std::string String::join(const std::vector &lst, const std::string &delim) { std::string ret; for(const auto &s : lst) { if(!ret.empty()) ret += delim; ret += s; } return ret; } 

Solução ligeiramente longa, mas não usa std::ostringstream , e não requer um hack para remover o último delimitador.

http://www.ideone.com/hW1M9

E o código:

 struct appender { appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic) { dest.reserve(2048); } void operator()(std::string const& copy) { dest.append(copy); if (--count) dest.append(1, delim); } char delim; mutable std::string& dest; mutable int count; }; void implode(const std::vector& elems, char delim, std::string& s) { std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size())); } 

apenas adicione !! String s = “”;

 for (int i = 0; i < doc.size(); i++) //doc is the vector s += doc[i]; 

Aqui está o que eu uso, simples e flexível

 string joinList(vector arr, string delimiter) { if (arr.empty()) return ""; string str; for (auto i : arr) str += i + delimiter; str = str.substr(0, str.size() - delimiter.size()); return str; } 

usando:

 string a = joinList({ "a", "bbb", "c" }, "!@#"); 

saída:

 a!@#bbb!@#c 

Em primeiro lugar, é necessário um stream de ostringstream class de stream para fazer a concatenação por várias vezes e salvar o problema subjacente de alocação excessiva de memory.

Código:

 string join(const vector& vec, const char* delim) { ostringstream oss; if(!string_vector.empty()) { copy(string_vector.begin(),string_vector.end() - 1, ostream_iterator(oss, delim.c_str())); } return oss.str(); } vector string_vector {"1", "2"}; string delim("->"); string joined_string = join(); // get "1->2" 

Explicação:

quando pensar, trate oss aqui como std::cout

quando queremos escrever:

std::cout << string_vector[0] << "->" << string_vector[1] << "->" ,

podemos usar as seguintes classs STL como ajuda:

ostream_iterator retorna um stream de saída com delimitadores anexados automaticamente sempre que você usar << .

por exemplo,

ostream my_cout = ostream_iterator(std::cout, "->")

wraps std:cout as my_cout

então cada vez que você my_cout << "string_vector[0]" ,

significa std::cout << "string_vector[0]" << "->"

Quanto a copy(vector.begin(), vector.end(), std::out);

significa std::cout << vector[0] << vector[1] (...) << vector[end]

tente isso, mas usando vetor ao invés de lista

 template  std::string listToString(std::list l){ std::stringstream ss; for(std::list::iterator it = l.begin(); it!=l.end(); ++it){ ss << *it; if(std::distance(it,l.end())>1) ss << ", "; } return "[" + ss.str()+ "]"; } 

Usando parte dessa resposta para outra pergunta, você se juntou a ela, com base em um separador sem uma vírgula final,

Uso:

 std::vector input_str = std::vector({"a", "b", "c"}); std::string result = string_join(input_str, ","); printf("%s", result.c_str()); /// a,b,c 

Código:

 std::string string_join(const std::vector& elements, const char* const separator) { switch (elements.size()) { case 0: return ""; case 1: return elements[0]; default: std::ostringstream os; std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator(os, separator)); os << *elements.rbegin(); return os.str(); } }