【问题标题】:std::ostream_iterator prevent last item from using the delimiter [duplicate]std::ostream_iterator 防止最后一项使用分隔符 [重复]
【发布时间】:2013-06-25 16:55:14
【问题描述】:

有没有办法使用 std::ostream_iterator (或类似的),以便不为最后一个元素放置分隔符?

#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, ","));
}

将打印

10,20,30,40,50,60,70,80,90,

我试图避免尾随分隔符。我要打印

10,20,30,40,50,60,70,80,90

当然,你可以使用循环:

for(auto it = ints.begin(); it != ints.end(); it++){
  std::cout << *it;
  if((it + 1) != ints.end()){           
    std::cout << ",";
  }
}

但鉴于 C++11 基于范围的循环,跟踪位置很麻烦。

int count = ints.size();
for(const auto& i : ints){
  std::cout << i;
  if(--count != 0){
    std::cout << ",";
  }     
}

我愿意使用 Boost。我查看了boost::algorithm::join(),但需要将整数复制到字符串,所以它是一个两行。

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,",");

理想情况下,我只想使用 std::algorithm 并且在范围内的最后一项上没有分隔符。

谢谢!

【问题讨论】:

  • infix_iterator 会回答这个问题吗?
  • 迭代到 std::copy 行中的倒数第二个值,而不是 ::end,然后打印最后一项。
  • @Cubbi [infix_iterator][stackoverflow.com/a/3497021/273767] 确实有效。很好,它是代码中 std::ostream_iterator 的替代品。

标签: c++ iostream stl-algorithm


【解决方案1】:

@Cubbi 在评论中指出这正是 infix_iterator 所做的

// 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,","));
}

打印:

10,20,30,40,50,60,70,80,90

【讨论】:

    【解决方案2】:

    copy 可以实现为:

    template<class InputIterator, class OutputIterator>
    OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
    {
      while (first!=last) {
      *result = *first;
      ++result; ++first;
      }
      return result;
    }
    

    ostream_iterator(输出迭代器)的赋值可以实现为:

    ostream_iterator<T,charT,traits>& operator= (const T& value) {
      *out_stream << value;
      if (delim!=0) *out_stream << delim;
      return *this;
    }
    

    因此,分隔符将附加到输出迭代器的每个分配上。为避免将分隔符附加到最后一个向量元素,应将最后一个元素分配给不带分隔符的输出迭代器,例如:

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

    结果:

    10,20,30,40,50,60,70,80,90
    

    【讨论】:

    • 再想一想,第二次拨打copy真的没有意义。
    【解决方案3】:

    这会更容易。不知道这就是你想要的

    #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】:

    使用 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);
    }
    

    【讨论】:

      猜你喜欢
      • 2012-12-21
      • 1970-01-01
      • 2019-11-10
      • 1970-01-01
      • 1970-01-01
      • 2018-11-28
      • 1970-01-01
      • 2017-04-23
      相关资源
      最近更新 更多