C ++vectorstring
我有一个vector<int>
容器有整数(例如{1,2,3,4}),我想转换为一个forms的string
"1,2,3,4"
在C ++中最干净的方法是什么? 在Python中,这是我将如何做到这一点:
>>> array = [1,2,3,4] >>> ",".join(map(str,array)) '1,2,3,4'
绝对不像Python那么优雅,但没有什么比C ++中的Python更优雅。
你可以使用一个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();
你也可以使用std::for_each
来代替。
使用std :: copy和std :: ostream_iterator,我们可以得到像Python一样优雅的东西。
#include <iostream> #include <sstream> #include <algorithm> #include <iterator> int main() { int array[] = {1,2,3,4}; std::copy(array, array+4, std::ostream_iterator<int>(std::cout,",")); }
看到这个问题 ,我写了一个小class。 这将不会打印结尾的逗号。 此外,如果我们假设C ++ 14将继续给我们基于范围的等效algorithm,如下所示:
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 }
另一个select是使用std::copy
和ostream_iterator
类:
#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);
也不如Python。 为此,我创build了一个join
函数:
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; }
然后像这样使用它:
std::string s=join(array.begin(), array.end(), std::string(","));
你可能会问为什么我通过迭代器。 那么,其实我想扭转arrays,所以我这样使用它:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
理想情况下,我想模板出来的地方,它可以推断字符types,并使用stringstream,但我还没有弄清楚。
你可以使用std :: accumulate。 考虑下面的例子
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); });
这只是一个试图解决1800信息对他的第二个缺乏通用性的解决scheme给出的谜题,而不是试图回答这个问题:
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(); }
适用于我的机器(TM)。
通过Boost和C ++ 11,可以这样做:
auto array = {1,2,3,4}; join(array | transformed(tostr), ",");
好吧,差不多。 以下是完整的示例:
#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; }
感谢Praetorian 。
你可以像这样处理任何值types:
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); };
很多模板/想法。 我的不是一般的或有效率的,但我也有同样的问题,想把这个东西简短而甜美的混合起来。 它赢得最短的行数… 🙂
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);
如果你想做std::cout << join(myVector, ",") << std::endl;
,你可以做这样的事情:
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)); }
注意,这个解决scheme直接连接到输出stream而不是创build一个辅助缓冲区,并且可以和任何带有operator <<的types一起工作到一个ostream上。
当你有一个vector<char*>
而不是一个vector<string>
时候, boost::algorithm::join()
也会失败。
我喜欢1800年的答案。 不过,我会将第一次迭代移出循环,因为if语句的结果只会在第一次迭代后更改一次
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; }
这当然可以减less到更less的陈述,如果你喜欢:
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; }
有一些有趣的尝试提供了一个优雅的解决scheme的问题。 我有一个想法,使用模板stream有效地回答OP的原始困境。 虽然这是一个旧的post,我希望未来的用户谁发现这一点会发现我的解决scheme是有益的。
首先,一些答案(包括被接受的答案)不提倡可重用性。 由于C ++没有提供在标准库(我见过)中连接string的优雅方法,因此创build一个灵活且可重用的方法变得非常重要。 这是我的注意事项:
// 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; } }
现在使用这个,你可以简单地做下面的事情:
// 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;
请注意,如何使用stream使得这个解决scheme非常灵活,因为我们可以将结果存储在一个stringstream中以便稍后回收,或者我们可以直接写入标准输出,文件,甚至是作为stream实现的networking连接。 正在打印的types必须简单地迭代并与源stream兼容。 STL提供了各种types的兼容的stream。 所以你可以真正去这个城市。 关于我的头顶,你的向量可以是int,float,double,string,unsigned int,SomeObject *等等。
我创build了一个帮助程序头文件来添加扩展连接支持。
只需将下面的代码添加到您的常规头文件并在需要时包含它。
用法示例:
/* 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; }
幕后代码:
#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); }
这是一个通用的C ++ 11解决scheme,可以让你做
int main() { vector<int> v {1,2,3}; cout << join(v, ", ") << endl; string s = join(v, '+').str(); }
代码是:
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);}
像@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 ; }
那么我们可以打电话如下:
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 ; }
就像python一样:
>>> " ".join( map(str, [1,2,3,4]) )
我使用这样的东西
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
我开始用@sbi的答案,但大部分时间结束了pipe道结果string到一个stream,所以创build了下面的解决scheme,可以pipe道到一个stream没有在内存中创build完整的string的开销。
它使用如下:
#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; }
其中string_join.h是:
#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); }
我写了下面的代码。 它基于C#string.join。 它适用于std :: string和std :: wstring以及许多types的向量。 (在评论中的例子)
像这样调用它:
std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5}; std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');
码:
// 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(); }