Как иметь дело с последней запятой при создании разделенной запятой строки?
Возможные дубликаты:
Не печатать пробел после последнего номера
Печать списков с запятыми С++
#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 записей в векторе! Если это все, что вы можете предложить, я бы скорее удалил последнюю запятую после того, как я прошел цикл.
Ответы
Ответ 1
Как насчет этого:
#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";
}
Не нужно добавлять дополнительные переменные и даже не зависит от повышения! На самом деле, помимо требования "без дополнительной переменной в цикле", можно сказать, что нет даже цикла:)
Ответ 2
Обнаружение последнего до последнего всегда сложно, обнаружение первого очень просто.
bool first = true;
stringstream ss;
BOOST_FOREACH(int i, VecInts)
{
if (!first) { ss << ","; }
first = false;
ss << i;
}
Ответ 3
Использование Karma от 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;
}
Ответ 4
Try:
if (ss.tellp ())
{
ss << ",";
}
ss << i;
В качестве альтернативы, если "if" вызывает у вас беспокойство:
char *comma = "";
BOOST_FOREACH(int i, VecInts)
{
ss << comma << i;
comma = ",";
}
Ответ 5
Лично мне нравится решение, которое не вызывает потенциальные выделения памяти (потому что строка растет больше, чем нужно). Дополнительный, если внутри тела цикла должен быть доступным благодаря буферизации целевой ветки, но я бы сделал это:
#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
Оптимально это работает для каждого выходного потока, любого контейнера, который имеет итераторы вперед, с любым инфишированием и с любым типом инфикса (интересным, например, при использовании широких строк).
Ответ 6
Мне нравится перемещать тест за пределы цикла.
Это нужно сделать только один раз. Сделайте это сначала.
Вот так:
if (!VecInts.empty())
{
ss << VecInts[0]
for(any loop = ++(VecInts.begin()); loop != VecInts.end(); ++loop)
{
ss << "," << *loop;
}
}
Ответ 7
Вы можете либо обрезать строку в конце, либо использовать одиночный цикл for вместо foreach и не конкатенировать на последней итерации
Ответ 8
Ну, если вы все равно форматируете в stringstream
, вы можете просто обрезать результирующую строку одним символом:
cout << ss.str().substr(0, ss.str().size() - 1);
Если строка пуста, то второй аргумент говорит -1, что означает все и не сбой, и если строка не пуста, она всегда заканчивается запятой.
Но если вы напрямую пишете поток вывода, я никогда не нашел ничего лучше, чем флаг first
.
То есть, если вы не хотите использовать join
из boost.string algo.
Ответ 9
Это будет работать
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
и петли внутри. Ваш if
будет ничем по сравнению с этим.
Ответ 10
cout << ss.str()<<"\b" <<" ";
Вы можете добавить "\ b" backspace
Это перезапишет дополнительные ",".
для примера:
int main()
{
cout<<"Hi";
cout<<'\b'; //Cursor moves 1 position backwards
cout<<" "; //Overwrites letter 'i' with space
}
Таким образом, выход будет
H