Как обращаться с последней запятой, когда делают разделенную запятыми строку? [дубликат]
Возможные дубликаты:
не печатайте пробел после последнего номера
печать списков с запятыми 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 ответов:
Как насчет этого:
#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
будет ничто по сравнению с этим.