Как обращаться с последней запятой, когда делают разделенную запятыми строку? [дубликат]


Возможные дубликаты:
не печатайте пробел после последнего номера
печать списков с запятыми C++

#include <vector>
#include <iostream>
#include <sstream>
#include <boost/foreach.hpp>
using namespace std;

int main()
{
   vector<int> VecInts;

   VecInts.push_back(1);
   VecInts.push_back(2);
   VecInts.push_back(3);
   VecInts.push_back(4);
   VecInts.push_back(5);

   stringstream ss;
   BOOST_FOREACH(int i, VecInts)
   {
      ss << i << ",";
   }

   cout << ss.str();

   return 0;
}

Это выводит: 1,2,3,4,5, Однако я хочу: 1,2,3,4,5

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

Я вижу, что есть некоторая путаница в том, что я подразумеваю под "элегантным": например, нет замедления "if-clause"в моем цикле. Представьте себе 100.000 записей в векторе! Если это все, что вы можете предложить, то я бы предпочел удалить последняя запятая после того, как я прошел через цикл.

10 32

10 ответов:

Как насчет этого:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>
#include <sstream>

int main()
{
   std::vector<int> v;

   v.push_back(1);
   v.push_back(2);
   v.push_back(3);
   v.push_back(4);
   v.push_back(5);

   std::ostringstream ss;

   std::copy(v.begin(), v.end() - 1, std::ostream_iterator<int>(ss, ", "));
   ss << v.back();

   std::cout << ss.str() << "\n";
}

Нет необходимости добавлять дополнительные переменные и даже не зависит от boost! На самом деле, в дополнение к требованию "нет дополнительной переменной в цикле", можно сказать, что нет даже цикла :)

Обнаружение предпоследнего всегда сложно, обнаружение первого очень легко.

bool first = true;
stringstream ss;
BOOST_FOREACH(int i, VecInts)
{
  if (!first) { ss << ","; }
  first = false;
  ss << i;
}

Использование кармы от Boost Spirit-имеет репутацию быстрого.

#include <iostream>
#include <vector>
#include <boost/spirit/include/karma.hpp>

int main()
{
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);

  using namespace boost::spirit::karma;
  std::cout << format(int_ % ',', v) << std::endl;
}

Попробуйте:

if (ss.tellp ())
{
   ss << ",";
}
ss << i;

Альтернативно, если "если" заставляет вас беспокоиться:

char *comma = "";
BOOST_FOREACH(int i, VecInts)
{
   ss << comma << i;
   comma = ",";
}

Лично мне нравится решение, которое не вызывает потенциального выделения памяти (потому что строка становится больше, чем нужно). Extra-if в теле цикла должен быть отслеживаемым благодаря целевой буферизации ветви, но я бы сделал так:

#include <vector>
#include <iostream>

int main () {
    using std::cout;
    typedef std::vector<int>::iterator iterator;

    std::vector<int> ints;    
    ints.push_back(5);
    ints.push_back(1);
    ints.push_back(4);
    ints.push_back(2);
    ints.push_back(3);


    if (!ints.empty()) {
        iterator        it = ints.begin();
        const iterator end = ints.end();

        cout << *it;
        for (++it; it!=end; ++it) {
            cout << ", " << *it;
        }
        cout << std::endl;
    }
}

Альтернативно, BYORA (принесите свой собственный многоразовый алгоритм):

// Follow the signature of std::getline. Allows us to stay completely
// type agnostic.
template <typename Stream, typename Iter, typename Infix>
inline Stream& infix (Stream &os, Iter from, Iter to, Infix infix_) {
    if (from == to) return os;
    os << *from;
    for (++from; from!=to; ++from) {
        os << infix_ << *from;
    }
    return os;
}

template <typename Stream, typename Iter>
inline Stream& comma_seperated (Stream &os, Iter from, Iter to) {
    return infix (os, from, to, ", ");
}

Так что

...
comma_seperated(cout, ints.begin(), ints.end()) << std::endl;

infix(cout, ints.begin(), ints.end(), "-") << std::endl;
infix(cout, ints.begin(), ints.end(), "> <") << std::endl;
...

Вывод:

5, 1, 4, 2, 3
5-1-4-2-3
5> <1> <4> <2> <3

Самое замечательное, что он работает для каждого выходного потока, любого контейнера, который имеет прямые итераторы, с любым инфиксом, и с любым типом инфикса (интересно, например, когда вы используете широкие строки).

Мне нравится выводить тест за пределы цикла.
Это нужно сделать только один раз. Так что сделай это первым.

Вот так:

if (!VecInts.empty())
{
    ss << VecInts[0]

    for(any loop = ++(VecInts.begin()); loop != VecInts.end(); ++loop)
    {
        ss << "," << *loop;
    }
}

Вы можете либо обрезать строку в конце, либо использовать single for loop вместо foreach и dont concatenate на последней итерации

Ну, если вы форматируете в stringstream в любом случае, вы можете просто обрезать полученную строку на один символ:

cout << ss.str().substr(0, ss.str().size() - 1);

Если строка пуста, то второй аргумент говорит -1, что означает все и не приводит к сбою, а если строка непустая, то она всегда заканчивается запятой.

Но если вы пишете в выходной поток напрямую, я никогда не находил ничего лучше, чем флаг first.

То есть, если вы не хотите использовать join из boost.строка algo .

Это сработает

stringstream ss;
BOOST_FOREACH(int const& i, VecInts)
{
   if(&i != &VecInts[0])
     ss << ", ";
   ss << i;
}

Я подозреваю, что под " элегантным "вы подразумеваете"без введения новой переменной". Но я думаю, что просто сделал бы это "неэлегантно", если бы не мог найти ничего другого. Это все еще ясно

stringstream ss;
bool comma = false;
BOOST_FOREACH(int i, VecInts)
{
   if(comma)
     ss << ", ";
   ss << i;
   comma = true;
}

Представьте себе 100.000 записей в векторе! Если это все, что вы можете предложить, я бы предпочел удалить последнюю запятую после того, как пройду весь цикл.

Вы говорите так, как будто печать ss << i - это одна машинная инструкция. Давай, исполняй это. выражение будет выполнять множество if'S и циклов внутри. Ваш if будет ничто по сравнению с этим.

cout << ss.str()<<"\b" <<" ";

Вы можете добавить "\b" backspace

Это приведет к перезаписи дополнительного ",".

Например:

int main()
{
    cout<<"Hi";
    cout<<'\b';  //Cursor moves 1 position backwards
    cout<<" ";   //Overwrites letter 'i' with space
}

Таким образом, выход будет

H