traducir inglés ingles español cómo como c++ string stl stdstring implode

c++ - inglés - Cómo implosionar un vector de cadenas en una cadena(de la manera elegante)



cómo se dice en inglés (15)

¿Qué hay de la simple solución estúpida?

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

Estoy buscando la manera más elegante de implosionar un vector de cadenas en una cadena. A continuación está la solución que estoy usando ahora:

static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s) { for (std::vector<std::string>::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<std::string>& elems, char delim) { std::string s; return implode(elems, delim, s); }

¿Hay otros por ahí?


Al usar parte de esta answer a otra pregunta, te uniste a esto, basado en un separador sin una coma al final,

Uso:

std::vector<std::string> input_str = std::vector<std::string>({"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<std::string>& 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<std::string>(os, separator)); os << *elements.rbegin(); return os.str(); } }


Aquí hay otro que no agrega el delimitador después del último elemento:

std::string concat_strings(const std::vector<std::string> &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 "";


Debería usar std::ostringstream lugar de std::string para compilar el resultado (luego puede llamar a su método str() al final para obtener una cadena, por lo que su interfaz no necesita cambiar, solo la s temporal).

A partir de ahí, puede cambiar a usar std::ostream_iterator , así:

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

Pero esto tiene dos problemas:

  1. delim ahora necesita ser un const char* , en lugar de un solo char . No es gran cosa.
  2. std::ostream_iterator escribe el delimitador después de cada elemento individual, incluido el último. Entonces, necesitarás borrar el último al final, o escribir tu propia versión del iterador que no tenga esta molestia. Valdría la pena hacer esto último si tienes un montón de código que necesita cosas como esta; de lo contrario, es mejor evitar todo el desastre (es decir, utilizar ostringstream pero no ostream_iterator ).

En primer lugar, se necesita una clase de secuencia ostringstream para hacer concatenaciones varias veces y salvar el problema subyacente de la asignación excesiva de memoria.

Código:

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

Explicación:

al pensar, trate oss aquí como std::cout

cuando queremos escribir:

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

podemos usar las siguientes clases de STL como ayuda:

ostream_iterator devuelve una secuencia de salida envuelta con delimitadores añadidos automáticamente cada vez que usa << .

por ejemplo,

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

wraps std:cout como my_cout

así que cada vez que my_cout << "string_vector[0]" ,

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

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

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


Especialmente con colecciones más grandes, debe evitar tener que verificar si aún está agregando el primer elemento o no para garantizar que no haya un separador final ...

Entonces, para la lista vacía o de un solo elemento, no hay ninguna iteración en absoluto.

Los rangos vacíos son triviales: devuelve "".

Un solo elemento o elemento múltiple se puede manejar perfectamente 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; }); };

Ejecución de muestra ( requiere C ++ 14 ): http://cpp.sh/8uspd


Esto es lo que uso, simple y flexible

string joinList(vector<string> 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; }

utilizando:

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

salida:

a!@#bbb!@#c


Porque me encantan los one-liners (son muy útiles para todo tipo de cosas raras, como verán al final), aquí hay una solución que usa std :: accumulate y 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; } )

Encuentro esta sintaxis útil con el operador de flujo, donde no quiero tener todo tipo de lógica extraña fuera del alcance de la operación de flujo, solo para hacer una simple unión de cadenas. Considere por ejemplo esta declaración de retorno del método que formatea una cadena usando operadores de flujo (usando std;):

return (dynamic_cast<ostringstream&>(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();


Solución ligeramente larga, pero no usa std::ostringstream , y no requiere un hack para eliminar el último delimitador.

http://www.ideone.com/hW1M9

Y el 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<std::string>& elems, char delim, std::string& s) { std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size())); }


Una versión que usa std::accumulate :

#include <numeric> #include <iostream> #include <string> 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"; }


Utilice boost::algorithm::join(..) :

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

Ver también esta pregunta .


prueba esto, pero usando el vector en lugar de la lista

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


solo agrega !! String s = "";

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



string join(const vector<string>& vec, const char* delim) { stringstream res; copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim)); return res.str(); }