我有一个整数向量 vector<int>
(例如 {1,2,3,4}),我想将其转换为形式如下的字符串:
"1,2,3,4"
在C++中最简洁的方法是什么? 在Python中,我会这样做:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
我有一个整数向量 vector<int>
(例如 {1,2,3,4}),我想将其转换为形式如下的字符串:
"1,2,3,4"
在C++中最简洁的方法是什么? 在Python中,我会这样做:
>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
在C++中没有什么能像Python那样优雅,但绝对不像Python那样优雅。
您可以使用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();
你也可以使用 std::for_each
。
std::string s = ss.str()
。如果你想要一个 const char*
,使用 s.c_str()
。请注意,虽然 ss.str().c_str()
语法上是正确的,但它会给你一个指向临时对象的 const char*
,在完整表达式结束后就会消失,这很危险。 - sbi#include <sstream>
,答案是不完整的。 - renadeen使用 std::for_each 和 lambda,你可以做一些有趣的事情。
#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=',';
});
}
请查看这个问题,其中包含我编写的一个小类。这将不会打印尾随逗号。此外,如果我们假设C++14将继续为我们提供像这样的算法的基于范围的等效物:
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
}
join
而不是加入OutputIterator
,但它基本上是相同的。 - Steve Jessop你可以使用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);
});
','
should be ","
- Matt另一种选择是使用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好用。为此,我创建了一个 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(","));
也许你会问为什么我传入了迭代器。其实,我想反转数组,所以我这样使用它:
std::string s=join(array.rbegin(), array.rend(), std::string(","));
join
函数也可以用于向量吗?能否给个例子,我是 C++ 新手。 - Noitidart使用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提供的帮助。
您可以像这样处理任何值类型:
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);
};
这只是尝试解决1800 INFORMATION的评论对他的第二个解决方案缺乏通用性的谜语,而不是回答问题的尝试:
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)。
operator<<
的类型)。当然,一个没有operator<<
的类型可能会导致非常令人困惑的错误消息。 - sbijoin(v.begin(), v.end(), ",")
。如果sep
参数是字符串字面量,模板参数推导就不能产生正确的结果。我尝试解决这个问题。同时提供了一个更现代化的基于范围的重载。 - zett42有很多模板/想法。我的不是那么通用或高效,但我也遇到了同样的问题,想把这个简短而甜美的东西加入其中。它以最少的行数获胜... :)
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);
substr(...)
,而是使用 pop_back()
来删除最后一个字符,这样会更加清晰和简洁。 - ifyalcinerstring s;
for (auto i : v)
s += (s.empty() ? "" : ",") + to_string(i);
std::stringstream
高效,因为stringstream
可以乐观地分配内存,导致性能为O(n.log(n)),而对于大小为n
的数组,该答案的性能为O(n²)。此外,stringstream
可能不会为to_string(i)
构建临时字符串。 - aberaud如果你想执行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));
}
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;
}
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;
}
++i
,除非他们真正需要 i++
,因为这是他们不会在有区别的情况下忘记的唯一方法。(顺便说一句,这也是我的情况。)他们之前学过Java,在那里所有种类的C语言习惯都很流行,他们花了几个月(每周1次讲座+实验课)时间,但最终大多数人学会了使用预增量的习惯。 - sbi