问题
Is there a way to use a std::ostream_iterator (or similar) such that the delimiter isn't placed for the last element?
#include <iterator>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
int main(int argc, char *argv[]) {
std::vector<int> ints = {10,20,30,40,50,60,70,80,90};
std::copy(ints.begin(),ints.end(),std::ostream_iterator<int>(std::cout, ","));
}
Will print
10,20,30,40,50,60,70,80,90,
I'm trying to avoid the trailing the delimiter. I want to print
10,20,30,40,50,60,70,80,90
Sure, you could use a loop:
for(auto it = ints.begin(); it != ints.end(); it++){
std::cout << *it;
if((it + 1) != ints.end()){
std::cout << ",";
}
}
But given C++11 range based loops this is cumbersome to track position.
int count = ints.size();
for(const auto& i : ints){
std::cout << i;
if(--count != 0){
std::cout << ",";
}
}
I'm open to using Boost. I looked into boost::algorithm::join() but needed to make a copy of the ints to strings so it was a two-liner.
std::vector<std::string> strs;
boost::copy(ints | boost::adaptors::transformed([](const int&i){return boost::lexical_cast<std::string>(i);}),std::back_inserter(strs));
std::cout << boost::algorithm::join(strs,",");
Ideally I'd just like to use a std::algorithm and not have the delimiter on the last item in the range.
Thanks!
回答1:
@Cubbi pointed out in a comment that is is exactly what infix_iterator does
// infix_iterator.h
//
// Lifted from Jerry Coffin's 's prefix_ostream_iterator
#if !defined(INFIX_ITERATOR_H_)
#define INFIX_ITERATOR_H_
#include <ostream>
#include <iterator>
template <class T,
class charT=char,
class traits=std::char_traits<charT> >
class infix_ostream_iterator :
public std::iterator<std::output_iterator_tag,void,void,void,void>
{
std::basic_ostream<charT,traits> *os;
charT const* delimiter;
bool first_elem;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_ostream<charT,traits> ostream_type;
infix_ostream_iterator(ostream_type& s)
: os(&s),delimiter(0), first_elem(true)
{}
infix_ostream_iterator(ostream_type& s, charT const *d)
: os(&s),delimiter(d), first_elem(true)
{}
infix_ostream_iterator<T,charT,traits>& operator=(T const &item)
{
// Here's the only real change from ostream_iterator:
// Normally, the '*os << item;' would come before the 'if'.
if (!first_elem && delimiter != 0)
*os << delimiter;
*os << item;
first_elem = false;
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator*() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++() {
return *this;
}
infix_ostream_iterator<T,charT,traits> &operator++(int) {
return *this;
}
};
#endif
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
std::vector<int> ints = {10,20,30,40,50,60,70,80,90};
std::copy(ints.begin(),ints.end(),infix_ostream_iterator<int>(std::cout,","));
}
Prints:
10,20,30,40,50,60,70,80,90
回答2:
copy
could be implement as:
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
The assignment to the ostream_iterator
(output iterator) could be implemented as:
ostream_iterator<T,charT,traits>& operator= (const T& value) {
*out_stream << value;
if (delim!=0) *out_stream << delim;
return *this;
}
So the delimiter will be appended on every assignment to the output iterator. To avoid the delimiter being appended to the last vector element, the last element should be assigned to an output iterator without delimiter, for example:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
std::vector<int> ints = {10,20,30,40,50,60,70,80,90};
std::copy(ints.begin(), ints.end()-1, std::ostream_iterator<int>(std::cout, ","));
std::copy(ints.end()-1, ints.end(), std::ostream_iterator<int>(std::cout));
std::cout << std::endl;
return 0;
}
Results in:
10,20,30,40,50,60,70,80,90
回答3:
this would be easier. Dunno this's what you want
#include<iostream>
#include<algorithm>
#include<vector>
#include<iterator>
int main()
{
std::vector<int> ints={10,20,30,40,50,60,70,80,90};
std::copy(ints.begin(),ints.end(),std::ostream_iterator<int> (std::cout,","));
std::cout<<(char)8;
}
回答4:
Use the erase method of std::string:
string join (const vector< vector<int> > data, const char* separator){
vector< vector<int> > result(data[0].size(), vector<int>(data.size()));
stringstream rowStream;
vector<string> rowVector;
for (size_t i = 0; i < data.size(); i++ ){
copy(data[i].begin(), data[i].begin() + data[i].size(), ostream_iterator<int>(rowStream, " "));
rowVector.push_back(rowStream.str().erase(rowStream.str().length()-1));
rowStream.str("");
rowStream.clear();
}
copy(rowVector.begin(), rowVector.begin() + rowVector.size(), ostream_iterator<string>(rowStream, separator));
return rowStream.str().erase(rowStream.str().length()-3);
}
来源:https://stackoverflow.com/questions/17355864/stdostream-iterator-prevent-last-item-from-using-the-delimiter