web-dev-qa-db-fra.com

Comment gérer la dernière virgule lors de la création d'une chaîne séparée par des virgules?

Doublons possibles: _
N'imprimez pas d'espace après le dernier numéro
Imprimer des listes avec des virgules 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;
}

Ceci affiche: 1,2,3,4,5, Cependant je veux: 1,2,3,4,5

Comment puis-je atteindre cet objectif d'une manière {élégante}?

Je vois qu'il y a une certaine confusion à propos de ce que je veux dire par "élégant": par exemple. pas de ralentissement "if-clause" dans ma boucle. Imaginez 100 000 entrées dans le vecteur! Si c'est tout ce que vous avez à offrir, je préférerais supprimer la dernière virgule après avoir parcouru la boucle.

32
AudioDroid

Que dis-tu de ça:

#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";
}

Pas besoin d'ajouter de variables supplémentaires et ne dépend même pas de boost! En fait, outre l'exigence "pas de variable supplémentaire dans la boucle", on pourrait dire qu'il n'y a même pas de boucle :)

30
mrm

Détecter l'avant-dernier est toujours délicat, détecter le premier est très facile.

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

Utilisation de Karma de Boost Spirit - a la réputation d’être rapide. 

#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;
}
10
Brian O'Kennedy

Essayer:

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

Alternativement, si le "si" vous inquiète:

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

Personnellement, j'aime une solution qui ne cause pas d'allocation de mémoire potentielle (car la chaîne devient plus grosse que nécessaire). Un extra-si dans le corps de la boucle devrait être traitable grâce à la mise en mémoire tampon de la cible de la branche, mais je le ferais:

#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 (apportez votre propre algorithme réutilisable):

// 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, ", ");
}

pour que

...
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;
...

sortie:

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

La chose intéressante est que cela fonctionne pour chaque flux de sortie, tout conteneur qui a des itérateurs, des infixes et des types d'infixes (intéressant par exemple lorsque vous utilisez des chaînes larges).

5
Sebastian Mach

J'aime déplacer le test en dehors de la boucle.
Cela ne doit être fait qu'une fois. Alors fais-le d'abord.

Comme ça:

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

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

Vous pouvez soit couper la chaîne à la fin, soit utiliser single pour la boucle au lieu de foreach et ne pas concaténer à la dernière itération

1
Ahmed Said

Eh bien, si vous formatez dans une stringstream de toute façon, vous pouvez simplement couper la chaîne résultante d'un caractère:

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

Si la chaîne est vide, le deuxième argument indique -1, ce qui signifie tout et ne tombe pas en panne. Si la chaîne n'est pas vide, elle se termine toujours par une virgule.

Mais si vous écrivez directement dans un flux de sortie, je n'ai jamais rien trouvé de mieux que l'indicateur first.

C'est à moins que vous ne souhaitiez utiliser join à partir de boost.string algo .

1
Jan Hudec

Cela fonctionnerait

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

Je suppose que par "élégant", vous voulez dire "sans introduire une nouvelle variable". Mais je pense que je le ferais simplement "non élégant" si je ne pouvais rien trouver d'autre. C'est encore clair

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

Imaginez 100 000 entrées dans le vecteur! Si c'est tout ce que vous avez à offrir, je préférerais supprimer la dernière virgule après avoir parcouru la boucle.

Vous dites que comme si imprimer ss << i est une instruction machine. Allez, exécuter cette expression exécutera beaucoup de if 's et de boucles à l'intérieur. Votre if ne sera rien comparé à cela. 

0