std::ostream_iterator prevent last item from using the delimiter [duplicate]

杀马特。学长 韩版系。学妹 提交于 2019-12-18 03:32:21

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!