Konvertiere einen Vektor in eine Zeichenkette

Ich habe einen vector -Container, der ganze Zahlen hat (zB {1,2,3,4}) und ich möchte in eine Zeichenkette des Formulars konvertieren

 "1,2,3,4" 

Was ist der sauberste Weg, das in C ++ zu tun? In Python würde ich es so machen:

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

Definitiv nicht so elegant wie Python, aber nichts ist so elegant wie Python in C ++.

Sie könnten einen 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(); 

Sie könnten stattdessen auch std::for_each verwenden.

Mit std :: copy und std :: ostream_iterator können wir etwas so elegantes wie Python bekommen.

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

Sehen Sie diese Frage für eine kleine class, die ich geschrieben habe. Dadurch wird das nachfolgende Komma nicht gedruckt. Auch wenn wir annehmen, dass C ++ 14 uns weiterhin bereichsbasierte Entsprechungen von Algorithmen wie folgt geben wird:

 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 } 

Eine andere Alternative ist die Verwendung von std::copy und der class 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); 

Auch nicht so schön wie Python. Zu diesem Zweck habe ich eine join function erstellt:

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

Dann benutzte es so:

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

Sie könnten fragen, warum ich die Iteratoren übergeben habe. Nun, eigentlich wollte ich das Array umkehren, also habe ich es so benutzt:

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

Im Idealfall möchte ich eine Vorlage erstellen, die auf den char-Typ verweist und String-Streams verwendet, aber das konnte ich noch nicht herausfinden.

Sie können std :: accumulate verwenden. Betrachten Sie das folgende Beispiel

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

Mit Boost und C ++ 11 könnte dies wie folgt erreicht werden:

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

Naja fast. Hier ist das vollständige Beispiel:

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

Kredit an Prätorianer .

Sie können mit jedem Werttyp wie diesem umgehen:

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

Dies ist nur ein Versuch, das Rätsel zu lösen, das von 1800 INFORMATIONs Bemerkung zu seiner zweiten Lösung ohne Generizität gegeben wurde, nicht ein Versuch, die Frage zu beantworten:

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

functioniert auf meiner Maschine (TM).

Viele Vorlagen / Ideen. Meins ist nicht so generisch oder effizient, aber ich hatte gerade das gleiche Problem und wollte dies als etwas kurz und gut in die Mischung casting. Es gewinnt auf der kürzesten Anzahl von Linien … 🙂

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

Wenn du std::cout < < join(myVector, ",") << std::endl; tun willst std::cout < < join(myVector, ",") << std::endl; , Sie können etwas tun wie:

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

Beachten Sie, dass diese Lösung den Join direkt in den Ausgabestream einfügt, anstatt einen sekundären Puffer zu erstellen, und mit allen Typen arbeitet, die über einen Operator auf einen Ostream verfügen.

Dies funktioniert auch, wenn boost::algorithm::join() fehlschlägt, wenn Sie einen vector anstelle eines vector .

Ich mag die Antwort von 1800. Allerdings würde ich die erste Iteration aus der Schleife verschieben, da sich das Ergebnis der if-statement nach der ersten Iteration nur einmal ändert

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

Dies kann natürlich auf weniger statementen reduziert werden, wenn Sie möchten:

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

Es gibt einige interessante Versuche, das Problem elegant zu lösen. Ich hatte die Idee, Vorlagen zu verwenden, um das ursprüngliche Dilemma des OP effektiv zu lösen. Obwohl dies ein alter Beitrag ist, hoffe ich, dass zukünftige Benutzer, die darüber stolpern, meine Lösung vorteilhaft finden werden.

Erstens, einige Antworten (einschließlich der akzeptierten Antwort) fördern die Wiederverwendbarkeit nicht. Da C ++ keine elegante Möglichkeit bietet, Strings in der Standardbibliothek (die ich gesehen habe) zu verbinden, ist es wichtig, ein solches zu erstellen, das flexibel und wiederverwendbar ist. Hier ist meine Einstellung dazu:

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

Um dies zu verwenden, könnten Sie einfach Folgendes tun:

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

Beachten Sie, dass die Verwendung von Streams diese Lösung unglaublich flexibel macht, da wir unser Ergebnis in einem Stringstream speichern können, um es später zurückzuerhalten, oder direkt in die Standardausgabe, eine Datei oder sogar in eine als Stream implementierte Netzwerkverbindung schreiben können. Der gedruckte Typ muss einfach iterativ und kompatibel mit dem Quelldatenstrom sein. STL bietet verschiedene Streams, die mit einer großen Anzahl von Typen kompatibel sind. Damit könntest du wirklich in die Stadt gehen. Ganz oben in meinem Kopf kann Ihr Vektor von int, float, double, string, unsigned int, SomeObject * und mehr sein.

Ich habe eine Helper-Header-Datei erstellt, um eine erweiterte Join-Unterstützung hinzuzufügen.

Fügen Sie einfach den folgenden Code zu Ihrer allgemeinen Header-Datei hinzu und fügen Sie sie bei Bedarf hinzu.

Anwendungsbeispiele:

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

Der Code hinter der Szene:

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

Hier ist eine generische C ++ 11-Lösung, mit der Sie das machen können

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

Der Code ist:

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

Im Folgenden finden Sie eine einfache und praktische Möglichkeit, Elemente in einem vector in einen string zu konvertieren:

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

Sie müssen #include für ostringstream #include ostringstream .

wie @capone es tat,

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

Dann können wir wie folgt anrufen:

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

genau wie Python:

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

Ich benutze so etwas

 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 

Ich begann mit der Antwort von @sbi, aber die meiste Zeit endete damit, dass die resultierende Zeichenfolge in einen Stream geleitet wurde, sodass die folgende Lösung erstellt wurde, die an einen Stream weitergeleitet werden konnte, ohne dass die gesamte Zeichenfolge im Speicher erstellt werden musste.

Es wird wie folgt verwendet:

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

Wo string_join.h ist:

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

Ich habe den folgenden Code geschrieben. Es basiert auf C # string.join. Es funktioniert mit std :: string und std :: wstring und vielen Arten von Vektoren. (Beispiele in Kommentaren)

Nenn es so:

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

Code:

 // 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(); }