Cómo implosionar un vector de cadenas en una cadena (de la manera elegante)

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

¿Hay otros por ahí?

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

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

Ver también esta pregunta .

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

(incluye , , y )

Si quiere tener un final limpio (sin delimitador final) eche un vistazo aquí

Debería usar std::ostringstream lugar de std::string para comstackr 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(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 ).

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 syntax útil con el operador de flujo, donde no quiero tener todo tipo de lógica extraña fuera del scope de la operación de flujo, solo para hacer una simple unión de cadenas. Considere por ejemplo esta statement de retorno del método que formatea una cadena usando operadores de flujo (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(); } 

Una versión 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"; } 

Aquí hay otro que no agrega el delimitador después del ú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 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

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

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

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

solo agrega !! String s = “”;

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

Esto es lo que uso, simple y flexible

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

utilizando:

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

salida:

 a!@#bbb!@#c 

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& 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" 

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(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]

prueba esto, pero usando el vector en lugar de la 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()+ "]"; } 

Al usar parte de esta respuesta a otra pregunta, te uniste a esto, basado en un separador sin una coma al 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(); } }