web-dev-qa-db-ja.com

コンマ区切りの文字列を作成するときに、最後のコンマを処理する方法は?

可能な重複:
最後の番号の後にスペースを印刷しない
カンマを使用したリストの印刷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

elegantの方法でそれをどのように達成できますか?

「エレガント」とは何を意味するのかについて、いくつか混乱があるようです。ループで「if句」の速度が低下することはありません。ベクターの100.000エントリを想像してみてください。それがあなたが提供しなければならないすべてであるならば、私はループを通過した後にむしろ最後のカンマを削除したいです。

33
AudioDroid

これはどう:

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

変数を追加する必要はなく、ブーストに依存することさえありません!実際には、「ループに追加の変数がない」という要件に加えて、ループすら存在しないと言えます:)

32
mrm

最後の前のものを検出することは常にトリッキーであり、最初のものを検出することは非常に簡単です。

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

Boost SpiritからKarmaを使用-高速であるとの評判があります。

#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

試してください:

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

あるいは、「もし」があなたを心配させているなら:

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

個人的には、潜在的なメモリ割り当てを引き起こさないソリューションが好きです(文字列が必要以上に大きくなるため)。ループ本体内の追加の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

すてきなことは、すべての出力ストリーム、前方反復子を持つ任意のコンテナー、任意の接頭辞、および任意の接頭辞タイプ(たとえば、ワイド文字列を使用する場合に興味深い)で機能します。

5
Sebastian Mach

テストをループの外に移動するのが好きです。
それは一度だけ実行する必要があります。最初にそれを行います。

このような:

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

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

最後に文字列をトリムするか、foreachの代わりに単一のforループを使用して、最後の反復で連結しないでください。

1
Ahmed Said

まあ、とにかくstringstreamにフォーマットする場合は、結果の文字列を1文字だけトリミングできます。

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

文字列が空の場合、2番目の引数は-1と表示されます。これはすべてを意味し、クラッシュしません。文字列が空でない場合は、常にカンマで終わります。

しかし、出力ストリームに直接書き込む場合、firstフラグよりも優れたものは見つかりませんでした。

boost.string algo からjoinを使用する場合を除きます。

1
Jan Hudec

これはうまくいくでしょう

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は1つの機械語命令です。さあ、その式を実行すると、たくさんのifとループが実行されます。あなたのifはそれと比較して何もありません。