Convierta un vector en una cadena

Tengo un contenedor vector que tiene números enteros (por ejemplo, {1,2,3,4}) y me gustaría convertirlo en una cadena de la forma

 "1,2,3,4" 

¿Cuál es la forma más limpia de hacer eso en C ++? En Python, así es como lo haría:

 >>> array = [1,2,3,4] >>> ",".join(map(str,array)) '1,2,3,4' 

Definitivamente no tan elegante como Python, pero nada es tan elegante como Python en C ++.

Podría usar un stringstream

 std::stringstream ss; for(size_t i = 0; i < v.size(); ++i) { if(i != 0) ss << ","; ss << v[i]; } std::string s = ss.str(); 

También puede hacer uso de std::for_each en std::for_each lugar.

Usando std :: copy y std :: ostream_iterator podemos obtener algo tan elegante como Python.

 #include  #include  #include  #include  int main() { int array[] = {1,2,3,4}; std::copy(array, array+4, std::ostream_iterator(std::cout,",")); } 

Vea esta pregunta para una pequeña clase que escribí. Esto no imprimirá la coma final. Además, si suponemos que C ++ 14 continuará proporcionándonos equivalentes basados ​​en rango de algoritmos como este:

 namespace std { // I am assuming something like this in the C++14 standard // I have no idea if this is correct but it should be trivial to write if it does not appear. template void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);} } using POI = PrefexOutputIterator; int main() { int array[] = {1,2,3,4}; std::copy(array, POI(std::cout, ",")); // ",".join(map(str,array)) // closer } 

Otra alternativa es el uso de std::copy y la clase ostream_iterator :

 #include  // ostream_iterator #include  // ostringstream #include  // copy std::ostringstream stream; std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream)); std::string s=stream.str(); s.erase(s.length()-1); 

Tampoco es tan bueno como Python. Para este propósito, creé una función de join :

 template  T join(const A &begin, const A &end, const T &t) { T result; for (A it=begin; it!=end; it++) { if (!result.empty()) result.append(t); result.append(*it); } return result; } 

Luego lo usé así:

 std::string s=join(array.begin(), array.end(), std::string(",")); 

Puede preguntar por qué pasé los iteradores. Bueno, en realidad quería invertir el conjunto, así que lo usé así:

 std::string s=join(array.rbegin(), array.rend(), std::string(",")); 

Idealmente, me gustaría crear una plantilla hasta el punto en que pueda inferir el tipo de caracteres y usar secuencias de cadenas, pero aún no me lo podía imaginar.

Puedes usar std :: accumulate. Considera el siguiente ejemplo

 if (v.empty() return std::string(); std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]), [](const std::string& a, int b){ return a + ',' + std::to_string(b); }); 

Con Boost y C ++ 11 esto podría lograrse así:

 auto array = {1,2,3,4}; join(array | transformed(tostr), ","); 

Bueno, casi. Aquí está el ejemplo completo:

 #include  #include  #include  #include  int main() { using boost::algorithm::join; using boost::adaptors::transformed; auto tostr = static_cast(std::to_string); auto array = {1,2,3,4}; std::cout << join(array | transformed(tostr), ",") << std::endl; return 0; } 

Crédito a pretoriano .

Puede manejar cualquier tipo de valor como este:

 template std::string join(Container const & container, std::string delimiter) { using boost::algorithm::join; using boost::adaptors::transformed; using value_type = typename Container::value_type; auto tostr = static_cast(std::to_string); return join(container | transformed(tostr), delimiter); }; 

Esto es solo un bash de resolver el enigma dado por la observación de 1800 INFORMATION sobre su segunda solución que carece de genericidad, no un bash de responder a la pregunta:

 template  Str join(It begin, const It end, const Str &sep) { typedef typename Str::value_type char_type; typedef typename Str::traits_type traits_type; typedef typename Str::allocator_type allocator_type; typedef std::basic_ostringstream ostringstream_type; ostringstream_type result; if(begin!=end) result << *begin++; while(begin!=end) { result << sep; result << *begin++; } return result.str(); } 

Works On My Machine (TM).

Montones de plantillas / ideas. El mío no es tan genérico ni eficiente, pero tuve el mismo problema y quería incluir esto en la mezcla como algo corto y dulce. Gana en el menor número de líneas … 🙂

 std::stringstream joinedValues; for (auto value: array) { joinedValues << value << ","; } //Strip off the trailing comma std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1); 

Si quieres hacer std::cout << join(myVector, ",") << std::endl; , puedes hacer algo como:

 template  class MyJoiner { C &c; T &s; MyJoiner(C &&container, T&& sep) : c(std::forward(container)), s(std::forward(sep)) {} public: template friend std::ostream& operator<<(std::ostream &o, MyJoiner const &mj); template friend MyJoiner join(C &&container, T&& sep); }; template std::ostream& operator<<(std::ostream &o, MyJoiner const &mj) { auto i = mj.c.begin(); if (i != mj.c.end()) { o << *i++; while (i != mj.c.end()) { o << mj.s << *i++; } } return o; } template MyJoiner join(C &&container, T&& sep) { return MyJoiner(std::forward(container), std::forward(sep)); } 

Tenga en cuenta que esta solución se une directamente al flujo de salida en lugar de crear un buffer secundario y funcionará con cualquier tipo que tenga un operador << en un ostream.

Esto también funciona cuando boost::algorithm::join() falla, cuando tienes un vector lugar de un vector .

Me gusta la respuesta de 1800. Sin embargo, movería la primera iteración fuera del ciclo ya que el resultado de la instrucción if solo cambia una vez después de la primera iteración.

 template  T join(const A &begin, const A &end, const T &t) { T result; A it = begin; if (it != end) { result.append(*it); ++it; } for( ; it!=end; ++it) { result.append(t); result.append(*it); } return result; } 

Por supuesto, esto se puede reducir a menos declaraciones si lo desea:

 template  T join(const A &begin, const A &end, const T &t) { T result; A it = begin; if (it != end) result.append(*it++); for( ; it!=end; ++it) result.append(t).append(*it); return result; } 

Hay algunos bashs interesantes para proporcionar una solución elegante al problema. Tuve la idea de usar secuencias de imagen para responder de manera efectiva al dilema original de OP. Aunque esta es una publicación anterior, espero que los futuros usuarios que tropiecen con esto encuentren que mi solución sea beneficiosa.

Primero, algunas respuestas (incluida la respuesta aceptada) no promueven la reutilización. Como C ++ no proporciona una manera elegante de unir cadenas en la biblioteca estándar (que he visto), se vuelve importante crear una que sea flexible y reutilizable. Aquí está mi oportunidad:

 // Replace with your namespace // namespace my { // Templated join which can be used on any combination of streams, iterators and base types // template  TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) { // A flag which, when true, has next iteration prepend our seperator to the stream // bool sep = false; // Begin iterating through our list // for (TIter i = begin; i != end; ++i) { // If we need to prepend a seperator, do it // if (sep) stream << seperator; // Stream the next value held by our iterator // stream << *i; // Flag that next loops needs a seperator // sep = true; } // As a convenience, we return a reference to the passed stream // return stream; } } 

Ahora para usar esto, simplemente puede hacer algo como lo siguiente:

 // Load some data // std::vector params; params.push_back(1); params.push_back(2); params.push_back(3); params.push_back(4); // Store and print our results to standard out // std::stringstream param_stream; std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl; // A quick and dirty way to print directly to standard out // my::join(std::cout, params.begin(), params.end(), ",") << std::endl; 

Tenga en cuenta cómo el uso de flujos hace que esta solución sea increíblemente flexible, ya que podemos almacenar nuestro resultado en un flujo de cadena para reclamarlo más tarde, o podemos escribir directamente en el archivo estándar, o incluso en una conexión de red implementada como una secuencia. El tipo que se imprime debe ser simplemente iterable y compatible con el flujo de origen. STL proporciona varias transmisiones que son compatibles con una amplia gama de tipos. Entonces, realmente podrías ir a la ciudad con esto. En la parte superior de mi cabeza, tu vector puede ser int, float, double, string, unsigned int, SomeObject *, y más.

Creé un archivo de encabezado auxiliar para agregar un soporte de unión extendida.

Simplemente agregue el código a continuación a su archivo de encabezado general e inclúyalo cuando sea necesario.

Ejemplos de uso:

 /* An example for a mapping function. */ ostream& map_numbers(ostream& os, const void* payload, generic_primitive data) { static string names[] = {"Zero", "One", "Two", "Three", "Four"}; os << names[data.as_int]; const string* post = reinterpret_cast(payload); if (post) { os << " " << *post; } return os; } int main() { int arr[] = {0,1,2,3,4}; vector vec(arr, arr + 5); cout << vec << endl; /* Outputs: '0 1 2 3 4' */ cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */ cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */ cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */ cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */ string post = "Mississippi"; cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */ return 0; } 

El código detrás de la escena:

 #include  #include  #include  #include  #include  using namespace std; #define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; } #define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T; typedef void* ptr; /** A union that could contain a primitive or void*, * used for generic function pointers. * TODO: add more primitive types as needed. */ struct generic_primitive { GENERIC_PRIMITIVE_CLASS_BUILDER(int); GENERIC_PRIMITIVE_CLASS_BUILDER(ptr); union { GENERIC_PRIMITIVE_TYPE_BUILDER(int); GENERIC_PRIMITIVE_TYPE_BUILDER(ptr); }; }; typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive); template class Join { public: Join(const T& begin, const T& end, const string& separator = " ", mapping_funct_t mapping = 0, const void* payload = 0): m_begin(begin), m_end(end), m_separator(separator), m_mapping(mapping), m_payload(payload) {} ostream& apply(ostream& os) const { T begin = m_begin; T end = m_end; if (begin != end) if (m_mapping) { m_mapping(os, m_payload, *begin++); } else { os << *begin++; } while (begin != end) { os << m_separator; if (m_mapping) { m_mapping(os, m_payload, *begin++); } else { os << *begin++; } } return os; } private: const T& m_begin; const T& m_end; const string m_separator; const mapping_funct_t m_mapping; const void* m_payload; }; template  Join join(const T& begin, const T& end, const string& separator = " ", ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0, const void* payload = 0) { return Join(begin, end, separator, mapping, payload); } template ostream& operator<<(ostream& os, const vector& vec) { return join(vec.begin(), vec.end()).apply(os); } template ostream& operator<<(ostream& os, const list& lst) { return join(lst.begin(), lst.end()).apply(os); } template ostream& operator<<(ostream& os, const set& s) { return join(s.begin(), s.end()).apply(os); } template ostream& operator<<(ostream& os, const Join& vec) { return vec.apply(os); } 

Aquí hay una solución genérica de C ++ 11 que te permitirá hacer

 int main() { vector v {1,2,3}; cout << join(v, ", ") << endl; string s = join(v, '+').str(); } 

El código es:

 template class Joiner { const Iterable& i_; const Sep& s_; public: Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {} std::string str() const {std::stringstream ss; ss << *this; return ss.str();} template friend std::ostream& operator<< (std::ostream& os, const Joiner& j); }; template std::ostream& operator<< (std::ostream& os, const Joiner& j) { auto elem = j.i_.begin(); if (elem != j.i_.end()) { os << *elem; ++elem; while (elem != j.i_.end()) { os << j.s_ << *elem; ++elem; } } return os; } template inline Joiner join(const I& i, const S& s) {return Joiner(i, s);} 

La siguiente es una forma simple y práctica de convertir elementos en un vector en una string :

 std::string join(const std::vector& numbers, const std::string& delimiter = ",") { std::ostringstream result; for (const auto number : numbers) { if (result.tellp() > 0) { // not first round result << delimiter; } result << number; } return result.str(); } 

Necesita #include para ostringstream .

como lo hizo @capone,

 std::string join(const std::vector &str_list , const std::string &delim=" ") { if(str_list.size() == 0) return "" ; return std::accumulate( str_list.cbegin() + 1, str_list.cend(), str_list.at(0) , [&delim](const std::string &a , const std::string &b) { return a + delim + b ; } ) ; } template  std::vector map(TT (*op)(ST) , const vector &ori_vec) { vector rst ; std::transform(ori_vec.cbegin() , ori_vec.cend() , back_inserter(rst) , [&op](const ST& val){ return op(val) ;} ) ; return rst ; } 

Entonces podemos llamar de la siguiente manera:

 int main(int argc , char *argv[]) { vector int_vec = {1,2,3,4} ; vector str_vec = map(to_string, int_vec) ; cout << join(str_vec) << endl ; return 0 ; } 

al igual que Python:

 >>> " ".join( map(str, [1,2,3,4]) ) 

Yo uso algo como esto

 namespace std { // for strings join string to_string( string value ) { return value; } } // namespace std namespace // anonymous { template< typename T > std::string join( const std::vector& values, char delimiter ) { std::string result; for( typename std::vector::size_type idx = 0; idx < values.size(); ++idx ) { if( idx != 0 ) result += delimiter; result += std::to_string( values[idx] ); } return result; } } // namespace anonymous 

Empecé con la respuesta de @ sbi, pero la mayor parte del tiempo terminé conectando la cadena resultante a una secuencia, por lo que creé la siguiente solución que se puede canalizar a una transmisión sin la sobrecarga de crear la secuencia completa en la memoria.

Se usa de la siguiente manera:

 #include "string_join.h" #include  #include  int main() { std::vector v = { 1, 2, 3, 4 }; // String version std::string str = join(v, std::string(", ")); std::cout << str << std::endl; // Directly piped to stream version std::cout << join(v, std::string(", ")) << std::endl; } 

Donde string_join.h es:

 #pragma once #include  #include  template class joined_strings { private: const It begin, end; Str sep; public: typedef typename Str::value_type char_type; typedef typename Str::traits_type traits_type; typedef typename Str::allocator_type allocator_type; private: typedef std::basic_ostringstream ostringstream_type; public: joined_strings(It begin, const It end, const Str &sep) : begin(begin), end(end), sep(sep) { } operator Str() const { ostringstream_type result; result << *this; return result.str(); } template friend ostream_type& operator<<( ostream_type &ostr, const joined_strings &joined) { It it = joined.begin; if(it!=joined.end) ostr << *it; for(++it; it!=joined.end; ++it) ostr << joined.sep << *it; return ostr; } }; template inline joined_strings join(It begin, const It end, const Str &sep) { return joined_strings(begin, end, sep); } template inline joined_strings join( Container container, const Str &sep) { return join(container.cbegin(), container.cend(), sep); } 

He escrito el siguiente código. Se basa en C # string.join. Funciona con std :: string y std :: wstring y muchos tipos de vectores. (ejemplos en comentarios)

Llámalo así:

  std::vector vVectorOfIds = {1, 2, 3, 4, 5}; std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L','); 

Código:

 // Generic Join template (mimics string.Join() from C#) // Written by RandomGuy (stackoverflow) 09-01-2017 // Based on Brian R. Bondy anwser here: // http://stackoverflow.com/questions/1430757/c-vector-to-string // Works with char, wchar_t, std::string and std::wstring delimiters // Also works with a different types of vectors like ints, floats, longs template auto Join(const std::vector &vToMerge, const D &delimiter) { // We use std::conditional to get the correct type for the stringstream (char or wchar_t) // stringstream = basic_stringstream, wstringstream = basic_stringstream using strType = std::conditional< std::is_same::value, char, std::conditional< std::is_same::value, char, wchar_t >::type >::type; std::basic_stringstream ss; for (size_t i = 0; i < vToMerge.size(); ++i) { if (i != 0) ss << delimiter; ss << vToMerge[i]; } return ss.str(); }