Mam vector<int>
kontener zawierający liczby całkowite (np. {1,2,3,4}) i chciałbym zamienić na ciąg znaków w postaci
"1,2,3,4"
Jaki jest najczystszy sposób na zrobienie tego w C ++? W Pythonie tak bym to zrobił:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
Odpowiedzi:
Zdecydowanie nie jest tak elegancki jak Python, ale nic nie jest tak eleganckie jak Python w C ++.
Możesz użyć
stringstream
...#include <sstream> //... std::stringstream ss; for(size_t i = 0; i < v.size(); ++i) { if(i != 0) ss << ","; ss << v[i]; } std::string s = ss.str();
Możesz także użyć
std::for_each
zamiast tego.źródło
std::string s = ss.str()
. Jeśli chceszconst char*
, użyjs.c_str()
. (Zauważ, że chociaż poprawna składniowo,ss.str().c_str()
da ci to,const char*
które wskazuje na tymczasowy, który przestanie istnieć pod koniec pełnego wyrażenia. To boli.)#include <sstream>
Używając std :: for_each i lambda możesz zrobić coś ciekawego.
#include <iostream> #include <sstream> int main() { int array[] = {1,2,3,4}; std::for_each(std::begin(array), std::end(array), [&std::cout, sep=' '](int x) mutable { out << sep << x; sep=','; }); }
Zobacz to pytanie dla małej klasy, którą napisałem. Nie spowoduje to wydrukowania końcowego przecinka. Również jeśli założymy, że C ++ 14 będzie nadal dawał nam odpowiedniki algorytmów w oparciu o zakresy, takie jak ten:
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<typename C, typename I> 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 }
źródło
Możesz użyć std ::umulate. Rozważmy następujący przykład
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); });
źródło
','
powinno być","
string
Klasa ma przeciążenie dla+
operatora, który może również akceptować znaki. Więc','
jest w porządku.Inną alternatywą jest użycie
std::copy
iostream_iterator
klasa:#include <iterator> // ostream_iterator #include <sstream> // ostringstream #include <algorithm> // copy std::ostringstream stream; std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream)); std::string s=stream.str(); s.erase(s.length()-1);
Również nie tak przyjemny jak Python. W tym celu stworzyłem
join
funkcję:template <class T, class A> 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; }
Następnie użyłem tego w ten sposób:
std::string s=join(array.begin(), array.end(), std::string(","));
Możesz zapytać, dlaczego zdałem w iteratorach. Cóż, właściwie chciałem odwrócić tablicę, więc użyłem tego w ten sposób:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
Idealnie chciałbym opracować szablon do punktu, w którym można wywnioskować typ znaku i użyć strumieni ciągów, ale nie mogłem jeszcze tego rozgryźć.
źródło
join
funkcja może być również używana z wektorami? Proszę podać przykład, jestem nowy w C ++.Z Boost i C ++ 11 można to osiągnąć w następujący sposób:
auto array = {1,2,3,4}; join(array | transformed(tostr), ",");
Cóż prawie. Oto pełny przykład:
#include <array> #include <iostream> #include <boost/algorithm/string/join.hpp> #include <boost/range/adaptor/transformed.hpp> int main() { using boost::algorithm::join; using boost::adaptors::transformed; auto tostr = static_cast<std::string(*)(int)>(std::to_string); auto array = {1,2,3,4}; std::cout << join(array | transformed(tostr), ",") << std::endl; return 0; }
Podziękowania dla Praetorian .
Możesz obsłużyć dowolny typ wartości, taki jak ten:
template<class Container> 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::string(*)(value_type)>(std::to_string); return join(container | transformed(tostr), delimiter); };
źródło
To tylko próba rozwiązania zagadki podanej w uwadze 1800 INFORMATION, dotyczącej jego drugiego rozwiązania pozbawionego ogólności, a nie próba odpowiedzi na pytanie:
template <class Str, class It> 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<char_type,traits_type,allocator_type> ostringstream_type; ostringstream_type result; if(begin!=end) result << *begin++; while(begin!=end) { result << sep; result << *begin++; } return result.str(); }
Działa na moim komputerze (TM).
źródło
operator<<
przeciążony). Oczywiście typ bezoperator<<
może powodować bardzo mylące komunikaty o błędach.join(v.begin(), v.end(), ",")
. Wyprowadzenie argumentu szablonu nie daje prawidłowego wyniku, jeślisep
argument jest literałem ciągu. Moja próba rozwiązania tego problemu . Zapewnia również bardziej nowoczesne przeciążenie oparte na zakresie.Wiele szablonów / pomysłów. Mój nie jest tak ogólny ani wydajny, ale miałem ten sam problem i chciałem wrzucić to do miksu jako coś krótkiego i słodkiego. Wygrywa na najmniejszej liczbie linii ... :)
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);
źródło
substr(...)
, użyj,pop_back()
aby usunąć ostatni znak, staje się wtedy znacznie bardziej przejrzysty i czysty.Jeśli chcesz
std::cout << join(myVector, ",") << std::endl;
, możesz zrobić coś takiego:template <typename C, typename T> class MyJoiner { C &c; T &s; MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {} public: template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj); template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep); }; template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> 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<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep) { return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep)); }
Zauważ, że to rozwiązanie wykonuje łączenie bezpośrednio do strumienia wyjściowego, zamiast tworzyć dodatkowy bufor i będzie działać z każdym typem, który ma operator << na ostream.
Działa to również w przypadku
boost::algorithm::join()
niepowodzenia, gdy maszvector<char*>
zamiast plikuvector<string>
.źródło
string s; for (auto i : v) s += (s.empty() ? "" : ",") + to_string(i);
źródło
std::stringstream
przypadku dużych tablic, ponieważstringstream
będzie w stanie optymistycznie alokować pamięć, co prowadzi do wydajności O (n.log (n)) zamiast O (n²) dla tablicy o rozmiarzen
dla tej odpowiedzi.stringstream
Może również nie tworzyć tymczasowych ciągów dlato_string(i)
.Podoba mi się odpowiedź z 1800 roku. Jednak przesunąłbym pierwszą iterację poza pętlę, ponieważ instrukcja if zmienia się tylko raz po pierwszej iteracji
template <class T, class A> 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; }
Można to oczywiście zredukować do mniejszej liczby stwierdzeń, jeśli chcesz:
template <class T, class A> 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; }
źródło
++i
z wyjątkiem sytuacji, gdy naprawdę tego potrzebowali,i++
ponieważ był to jedyny sposób, w jaki nie zapomną o tym, kiedy to zrobi różnicę. (Swoją drogą, tak samo było ze mną). Uczyli się wcześniej Javy, gdzie wszystkie rodzaje C-izmów są modne i zajęło im to kilka miesięcy (1 wykład + praca laboratoryjna tygodniowo), ale ostatecznie większość nauczyli się nawyku używania preinkrementacji.Istnieje kilka interesujących prób eleganckiego rozwiązania problemu. Wpadłem na pomysł, aby użyć strumieni opartych na szablonach, aby skutecznie odpowiedzieć na pierwotny dylemat OP. Chociaż jest to stary post, mam nadzieję, że przyszli użytkownicy, którzy się na to natkną, uznają moje rozwiązanie za korzystne.
Po pierwsze, niektóre odpowiedzi (w tym zaakceptowana odpowiedź) nie promują możliwości ponownego użycia. Ponieważ C ++ nie zapewnia eleganckiego sposobu łączenia ciągów w bibliotece standardowej (co widziałem), ważne staje się utworzenie takiej, która jest elastyczna i nadaje się do wielokrotnego użytku. Oto moja szansa:
// Replace with your namespace // namespace my { // Templated join which can be used on any combination of streams, iterators and base types // template <typename TStream, typename TIter, typename TSeperator> 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; } }
Teraz, aby z tego skorzystać, możesz po prostu zrobić coś takiego:
// Load some data // std::vector<int> 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;
Zwróć uwagę, jak użycie strumieni sprawia, że to rozwiązanie jest niezwykle elastyczne, ponieważ możemy przechowywać nasz wynik w strumieniu ciągów, aby go później odzyskać, lub możemy pisać bezpośrednio do standardowego wyjścia, pliku lub nawet do połączenia sieciowego zaimplementowanego jako strumień. Drukowany typ musi być po prostu powtarzalny i zgodny ze strumieniem źródłowym. STL zapewnia różne strumienie, które są zgodne z szeroką gamą typów. Więc naprawdę możesz pojechać z tym do miasta. U mnie, twój wektor może mieć postać int, float, double, string, unsigned int, SomeObject * i więcej.
źródło
Utworzyłem plik nagłówkowy pomocnika, aby dodać rozszerzoną obsługę złączeń.
Po prostu dodaj poniższy kod do ogólnego pliku nagłówkowego i dołącz go w razie potrzeby.
Przykłady użycia:
/* 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<const string*>(payload); if (post) { os << " " << *post; } return os; } int main() { int arr[] = {0,1,2,3,4}; vector<int> 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; }
Kod za sceną:
#include <iostream> #include <vector> #include <list> #include <set> #include <unordered_set> 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<typename T> 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 <typename T> Join<T> join(const T& begin, const T& end, const string& separator = " ", ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0, const void* payload = 0) { return Join<T>(begin, end, separator, mapping, payload); } template<typename T> ostream& operator<<(ostream& os, const vector<T>& vec) { return join(vec.begin(), vec.end()).apply(os); } template<typename T> ostream& operator<<(ostream& os, const list<T>& lst) { return join(lst.begin(), lst.end()).apply(os); } template<typename T> ostream& operator<<(ostream& os, const set<T>& s) { return join(s.begin(), s.end()).apply(os); } template<typename T> ostream& operator<<(ostream& os, const Join<T>& vec) { return vec.apply(os); }
źródło
Oto ogólne rozwiązanie C ++ 11, które Ci na to pozwoli
int main() { vector<int> v {1,2,3}; cout << join(v, ", ") << endl; string s = join(v, '+').str(); }
Kod to:
template<typename Iterable, typename Sep> 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<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j); }; template<typename I, typename S> std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& 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<typename I, typename S> inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
źródło
Poniżej przedstawiono prosty i praktyczny sposób konwersji elementów z a
vector
na astring
:std::string join(const std::vector<int>& 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(); }
Trzeba
#include <sstream>
zaostringstream
.źródło
Rozszerzanie przy próbie @sbi w ogólnym rozwiązaniu, które nie jest ograniczone do
std::vector<int>
lub określonego typu zwracanego ciągu. Poniższy kod można wykorzystać w następujący sposób:std::vector<int> vec{ 1, 2, 3 }; // Call modern range-based overload. auto str = join( vec, "," ); auto wideStr = join( vec, L"," ); // Call old-school iterator-based overload. auto str = join( vec.begin(), vec.end(), "," ); auto wideStr = join( vec.begin(), vec.end(), L"," );
W oryginalnym kodzie dedukcja argumentów szablonu nie działa w celu uzyskania prawidłowego zwracanego typu ciągu, jeśli separator jest literałem ciągu (jak w przykładach powyżej). W tym przypadku definicje typu, takie jak
Str::value_type
w treści funkcji, są nieprawidłowe. W kodzie założono, żeStr
jest to zawsze typ podobny do typustd::basic_string
, więc w przypadku literałów ciągów jest to oczywiste.Aby to naprawić, poniższy kod próbuje wywnioskować tylko typ znaku z argumentu separatora i używa go do utworzenia domyślnego zwracanego typu ciągu. Osiąga się to za pomocą
boost::range_value
, który wyodrębnia typ elementu z danego typu zakresu .#include <string> #include <sstream> #include <boost/range.hpp> template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt > Str join( InputIt first, const InputIt last, const Sep& sep ) { using char_type = typename Str::value_type; using traits_type = typename Str::traits_type; using allocator_type = typename Str::allocator_type; using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >; ostringstream_type result; if( first != last ) { result << *first++; } while( first != last ) { result << sep << *first++; } return result.str(); }
Teraz możemy łatwo zapewnić przeciążenie oparte na zakresie, które po prostu przekazuje do przeciążenia opartego na iteratorze:
template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange> Str join( const InputRange &input, const Sep &sep ) { // Include the standard begin() and end() in the overload set for ADL. This makes the // function work for standard types (including arrays), aswell as any custom types // that have begin() and end() member functions or overloads of the standalone functions. using std::begin; using std::end; // Call iterator-based overload. return join( begin(input), end(input), sep ); }
Demo na żywo w Coliru
źródło
tak jak @capone,
std::string join(const std::vector<std::string> &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 <typename ST , typename TT> std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec) { vector<TT> rst ; std::transform(ori_vec.cbegin() , ori_vec.cend() , back_inserter(rst) , [&op](const ST& val){ return op(val) ;} ) ; return rst ; }
Następnie możemy zadzwonić w następujący sposób:
int main(int argc , char *argv[]) { vector<int> int_vec = {1,2,3,4} ; vector<string> str_vec = map<int,string>(to_string, int_vec) ; cout << join(str_vec) << endl ; return 0 ; }
tak jak python:
>>> " ".join( map(str, [1,2,3,4]) )
źródło
Używam czegoś takiego
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<T>& values, char delimiter ) { std::string result; for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx ) { if( idx != 0 ) result += delimiter; result += std::to_string( values[idx] ); } return result; } } // namespace anonymous
źródło
Zacząłem od odpowiedzi @ sbi, ale większość czasu kończyło się na przesłaniu otrzymanego ciągu do strumienia, więc stworzyłem poniższe rozwiązanie, które można przesłać do strumienia bez narzutu związanego z tworzeniem pełnego ciągu w pamięci.
Jest używany w następujący sposób:
#include "string_join.h" #include <iostream> #include <vector> int main() { std::vector<int> 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; }
Gdzie string_join.h to:
#pragma once #include <iterator> #include <sstream> template<typename Str, typename It> 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<char_type, traits_type, allocator_type> 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<typename ostream_type> friend ostream_type& operator<<( ostream_type &ostr, const joined_strings<Str, It> &joined) { It it = joined.begin; if(it!=joined.end) ostr << *it; for(++it; it!=joined.end; ++it) ostr << joined.sep << *it; return ostr; } }; template<typename Str, typename It> inline joined_strings<Str, It> join(It begin, const It end, const Str &sep) { return joined_strings<Str, It>(begin, end, sep); } template<typename Str, typename Container> inline joined_strings<Str, typename Container::const_iterator> join( Container container, const Str &sep) { return join(container.cbegin(), container.cend(), sep); }
źródło
Napisałem następujący kod. Jest oparty na C # string.join. Działa z std :: string i std :: wstring oraz wieloma typami wektorów. (przykłady w komentarzach)
Nazwij to tak:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5}; std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
Kod:
// 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<typename T, typename D> auto Join(const std::vector<T> &vToMerge, const D &delimiter) { // We use std::conditional to get the correct type for the stringstream (char or wchar_t) // stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t> using strType = std::conditional< std::is_same<D, std::string>::value, char, std::conditional< std::is_same<D, char>::value, char, wchar_t >::type >::type; std::basic_stringstream<strType> ss; for (size_t i = 0; i < vToMerge.size(); ++i) { if (i != 0) ss << delimiter; ss << vToMerge[i]; } return ss.str(); }
źródło
Oto prosty sposób na konwersję wektora liczb całkowitych na łańcuchy.
#include <bits/stdc++.h> using namespace std; int main() { vector<int> A = {1, 2, 3, 4}; string s = ""; for (int i = 0; i < A.size(); i++) { s = s + to_string(A[i]) + ","; } s = s.substr(0, s.length() - 1); //Remove last character cout << s; }
źródło
dołącz za pomocą funkcji szablonu
Użyłem a,
template
function
aby połączyćvector
elementy i usunąłem niepotrzebneif
stwierdzenie, przechodząc przez iterację tylko od pierwszego do przedostatniego elementu wvector
, a następnie dołączając do ostatniego elementu pofor
pętli. Eliminuje to również potrzebę stosowania dodatkowego kodu w celu usunięcia dodatkowego separatora na końcu połączonego ciągu. Nie ma więcif
instrukcji spowalniających iterację i zbędnego separatora, który wymaga uporządkowania.To daje elegancki wywołanie funkcji przyłączyć się
vector
SIĘ GOstring
,integer
lubdouble
itpNapisałem dwie wersje: jedna zwraca ciąg; druga pisze bezpośrednio do strumienia.
#include <iostream> #include <sstream> #include <string> #include <vector> using namespace std; // Return a string of joined vector items. template<typename T> string join(const vector<T>& v, const string& sep) { ostringstream oss; const auto LAST = v.end() - 1; // Iterate through the first to penultimate items appending the separator. for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p) { oss << *p << sep; } // Join the last item without a separator. oss << *LAST; return oss.str(); } // Write joined vector items directly to a stream. template<typename T> void join(const vector<T>& v, const string& sep, ostream& os) { const auto LAST = v.end() - 1; // Iterate through the first to penultimate items appending the separator. for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p) { os << *p << sep; } // Join the last item without a separator. os << *LAST; } int main() { vector<string> strings { "Joined", "from", "beginning", "to", "end" }; vector<int> integers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; vector<double> doubles{ 1.2, 3.4, 5.6, 7.8, 9.0 }; cout << join(strings, "... ") << endl << endl; cout << join(integers, ", ") << endl << endl; cout << join(doubles, "; ") << endl << endl; join(strings, "... ", cout); cout << endl << endl; join(integers, ", ", cout); cout << endl << endl; join(doubles, "; ", cout); cout << endl << endl; return 0; }
Wynik
źródło