Как изменить позицию разделителя? - PullRequest
3 голосов
/ 08 сентября 2011

Этот пример:

#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    int v[] = { 1, 2, 3 };

    std::copy( &v[0], &v[3], std::ostream_iterator< int >( std::cout, "\n " ) );
}

производит следующий вывод:

1
 2
 3
 

Есть ли способ изменить пример, чтобы заставить его производить следующий вывод?


 1
 2
 3

PS Я знаю, что мог бы использовать цикл for, но меня интересует решение, которое использует алгоритмы и итераторы.

Ответы [ 5 ]

3 голосов
/ 08 сентября 2011

Нет способа сделать это с std::ostream_iterator. (ИМХО там должно быть, но это не там.) Если вы не против написать очень маленький функция или класс, Вы можете использовать std::transform, например:

struct FormatString
{
    std::string operator()( std::string const& original ) const
    {
        return ' ' + original + '\n';
    }
};

//  ...
std::transform(
    v.begin(), v.end(),
    std::ostream_iterator<std::string>( std::cout ), 
    FormatString() );

Если у вас есть C ++ 11, вы можете использовать лямбду для FormatString.

Я считаю, что это происходит достаточно часто, поэтому я написал PatsubstTransformer - функциональный объект, который в основном реализует функцию $(patsubst...) GNU make Так что я бы просто должен написать:

std::transform(
    v.begin(), v.end(),
    std::ostream_iterator<std::string>( std::cout ),
    PatsubstTransformer( "%", " %\n" ) );

Я часто этим пользуюсь. (Я также нахожу, используя std::transform больше уместнее std::copy, так как то, что я вывожу, это преобразование.)

3 голосов
/ 08 сентября 2011

Если вы хотите использовать C ++ 11, вы можете использовать лямбду.

например, вот так:

int v[] = { 1, 2, 3};

std::for_each( &v[0], &v[3], [](int i){ std::cout << " " << i << "\n";} );
3 голосов
/ 08 сентября 2011

Используйте std::cout << " " вместо std::cout как:

std::copy(v, v+3, std::ostream_iterator<int>(std::cout << " ", "\n " ) );

Здесь выражение std::cout << " " сначала определяет, какой выводит один пробел на выход, а оцененное значение, равное std::ostream&, передается std::ostream_iterator

Теперь вывод будет выровнен правильно:

 1
 2
 3

Рабочий код: http://www.ideone.com/kSdpk

Кстати, не пишите &v[3]. Это вызывает Неопределенный bevahior . Пишите v+3.

2 голосов
/ 08 сентября 2011

Нет, не совсем. ostream_iterator не настраивается таким образом.

Так что вам придется использовать «обходной путь» перед пробелом, как в других ответах, и вручную отрубить эту последнюю строку.


BTW Было отмечено , что &v[3], строго говоря, вызывает неопределенное поведение из-за неявного разыменования в подвыражении v[3]. Предпочитаю &v[0]+3 (или просто v+3) & mdash; «иметь» указатель на один за концом массива - это нормально, если только он не разыменован.


Вы можете создать свой собственный вид ostream_iterator, который делает это, как показано в следующем примере.

Да, это многословно; тем не менее, вы также можете изменить его по своему усмотрению:

#include <iostream>
#include <iterator>
#include <algorithm>

template <class T, class charT = char, class traits = std::char_traits<charT> >
struct ostream_iterator_x
  : std::iterator<std::output_iterator_tag, void, void, void, void> {

    typedef charT char_type;
    typedef traits traits_type;
    typedef std::basic_ostream<charT,traits> ostream_type;

    ostream_iterator_x(ostream_type& s, const charT* pre = 0, const charT* post = 0)
       :    s(s)
       ,  pre(pre)
       , post(post) {};

    ostream_iterator_x(const ostream_iterator_x& x)
       :    s(x.s)
       ,  pre(x.pre)
       , post(x.post) {};

    ~ostream_iterator_x() {}

    ostream_iterator_x& operator=(const T& value) {
       if (pre  != 0) s << pre;
       s << value;
       if (post != 0) s << post;

       return *this;
    }

    ostream_iterator_x& operator*()     { return *this; }
    ostream_iterator_x& operator++()    { return *this; }
    ostream_iterator_x& operator++(int) { return *this; }

  private:
    ostream_type& s;
    const charT* pre;
    const charT* post;
};

int main()
{
    int v[] = { 1, 2, 3 };
    std::copy(v, v+3, ostream_iterator_x<int>(std::cout, " ", "\n"));
}

// Output:
//  1
//  2
//  3

(я использовал [n3290: 24.6/2], чтобы определить членов и базовую спецификацию, необходимую для того, чтобы это работало и соответствовало стандартам.)

Живая демоверсия.

2 голосов
/ 08 сентября 2011

Выведите один пробел перед std::copy.

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