Domanda

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!

È stato utile?

Soluzione

@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

Altri suggerimenti

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

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;
}

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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top