Соединить вектор строк в std :: ostream (например, boost :: join) - PullRequest
4 голосов
/ 12 марта 2012

У меня есть вектор строк, и я хочу вывести его в поток (фактически, поток файлов). И я хочу иметь разделитель между векторными элементами. Есть способ использовать стандарт ostream_iterator

std::vector <std::string> strs;
std::ostream_iterator<std::string> out_file_iterator ( out_file, delim );
std::copy ( strs.begin(), strs.end(), out_file_iterator );

Мне не понравился этот способ, потому что после каждого элемента есть текст delim, но мне не нужно иметь delim после последнего элемента. Я хотел бы использовать что-то вроде boost::join. Однако boost::join возвращает строку и мой вектор слишком велик, чтобы вывести его в строку.

Какой самый элегантный способ достичь моей цели?

Ответы [ 5 ]

3 голосов
/ 12 марта 2012

Самым элегантным было бы написать собственный цикл. Или отдельная функция.

template<class Stream, class InIt>
void print_range(Stream& s, InIt first, InIt last, char const* delim = "\n"){
  if(first == last)
    return;
  s << *first++;
  for(; first != last; ++first){
    s << delim << *first;
  }
}
3 голосов
/ 12 марта 2012

Один из способов, который работает, - обрабатывать последний отдельно.Но не думайте, что это очень элегантно .Конечно, вы можете обернуть безобразие своей собственной функцией join.

assert(strs.size() > 0);
std::ostream_iterator<std::string> out_file_iterator ( out_file, delim );
std::copy ( strs.begin(), strs.end()-1, out_file_iterator );
out_file << strs.back();
2 голосов
/ 12 марта 2012

Для общего решения (не проверено):

template<class T>
class ostream_join_iterator {
public:

    // Construct like an ostream_iterator.
    ostream_join_iterator(std::ostream& stream,
        const std::string& delimiter = "")
        : stream(stream), delimiter(delimiter), first(true) {}

    // Behave like an output iterator.
    ostream_join_iterator& operator++()    { return *this; }
    ostream_join_iterator& operator++(int) { return *this; }
    ostream_join_iterator& operator*()     { return *this; }

    // Output a delimiter before all but the first element.
    template<class T>
    ostream_join_iterator& operator=(const T& value) {
        if (!first) {
            stream << delimiter;
        } else {
            first = false;
        }
        stream << value;
        return *this;
    }

private:

    std::ostream& stream;
    const std::string delimiter;
    bool first;

};

Вы можете использовать его как обычный std::ostream_iterator:

std::copy(strings.begin(), strings.end(),
    ostream_join_iterator<std::string>(file, delimiter));
0 голосов
/ 13 марта 2012

Есть идея с Итератором ввода функции Boost

using namespace std;

struct generator {
  typedef string result_type;
  generator(result_type _delim) : delim_(_delim), is_first_(true) {}
  result_type operator () () { 
    if (!is_first_)
      return delim_; 
    is_first_ = false; 
    return ""; 
  }
private:
  result_type delim_;
  bool is_first_;
};

template<class T>
struct reverse_plus : public binary_function<T, T, T> {
  T operator()(const T& _lhs, const T& _rhs) const { return (_rhs + _lhs); }
};

// output to file stream
transform
( strs.begin()
, strs.end()
, boost::make_function_input_iterator(generator(" , "), boost::infinite())
, ostream_iterator<string> out_file_iterator(out_file)
, reverse_plus<string>()
);
0 голосов
/ 13 марта 2012

Это идея с функтором

using namespace std;

struct add_delim_t {
  add_delim_t(const char *_delim) : delim_(_delim), is_first_(true) {}
  string operator () (const string &_val) {
    if (is_first_) { is_first_ = false; return _val; } else return delim_ + _val;
  }
private:
  const string delim_;
  bool is_first_;
};

transform(s.begin(), s.end(), ostream_iterator<string>(cout), add_delim_t(" , "));

Проблема с этим решением в том, что он использует предикат statefull. Теоретически это означает UB.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...