63

私は他の言語でこれを行う方法を知っていますが、ここで使用することを余儀なくされている C++ では知りません。

リストとして出力する一連の文字列 ( keywords) がoutあり、文字列の間にはコンマが必要ですが、末尾のコンマは必要ありません。たとえば、Java では、文字列を作成StringBuilderした後、a を使用して末尾のカンマを削除します。C++でどうすればいいですか?

auto iter = keywords.begin();
for (iter; iter != keywords.end( ); iter++ )
{
    out << *iter << ", ";
}
out << endl;

私は最初にそれを行うために次のブロックを挿入しようとしました(ここにカンマ印刷を移動します):

if (iter++ != keywords.end())
    out << ", ";
iter--;
4

32 に答える 32

56

infix_iteratorを使用します。

// infix_iterator.h 
// 
// Lifted from Jerry Coffin's 's prefix_ostream_iterator 
#if !defined(INFIX_ITERATOR_H_) 
#define  INFIX_ITERATOR_H_ 
#include <ostream> 
#include <iterator> 
template <class T, 
          class charT=char, 
          class traits=std::char_traits<charT> > 
class infix_ostream_iterator : 
    public std::iterator<std::output_iterator_tag,void,void,void,void> 
{ 
    std::basic_ostream<charT,traits> *os; 
    charT const* delimiter; 
    bool first_elem; 
public: 
    typedef charT char_type; 
    typedef traits traits_type; 
    typedef std::basic_ostream<charT,traits> ostream_type; 
    infix_ostream_iterator(ostream_type& s) 
        : os(&s),delimiter(0), first_elem(true) 
    {} 
    infix_ostream_iterator(ostream_type& s, charT const *d) 
        : os(&s),delimiter(d), first_elem(true) 
    {} 
    infix_ostream_iterator<T,charT,traits>& operator=(T const &item) 
    { 
        // Here's the only real change from ostream_iterator: 
        // Normally, the '*os << item;' would come before the 'if'. 
        if (!first_elem && delimiter != 0) 
            *os << delimiter; 
        *os << item; 
        first_elem = false; 
        return *this; 
    } 
    infix_ostream_iterator<T,charT,traits> &operator*() { 
        return *this; 
    } 
    infix_ostream_iterator<T,charT,traits> &operator++() { 
        return *this; 
    } 
    infix_ostream_iterator<T,charT,traits> &operator++(int) { 
        return *this; 
    } 
};     
#endif 

使用法は次のようになります。

#include "infix_iterator.h"

// ...
std::copy(keywords.begin(), keywords.end(), infix_iterator(out, ","));
于 2010-08-16T20:24:48.953 に答える
40

間もなく登場する実験的な C++17 対応コンパイラでは、以下を使用できますstd::experimental::ostream_joiner

#include <algorithm>
#include <experimental/iterator>
#include <iostream>
#include <iterator>

int main()
{
    int i[] = {1, 2, 3, 4, 5};
    std::copy(std::begin(i),
              std::end(i),
              std::experimental::make_ostream_joiner(std::cout, ", "));
}

GCC 6.0 SVNClang 3.9 SVNを使用したライブ サンプル

于 2016-03-14T21:18:08.877 に答える
30

誰もがこれをwhileループで行うことに決めたので、forループで例を示します。

for (iter = keywords.begin(); iter != keywords.end(); iter++) {
  if (iter != keywords.begin()) cout << ", ";
  cout << *iter;
}
于 2010-08-16T20:29:21.580 に答える
24

漠然と通常の出力ストリームを想定しているため、空の文字列を書き込んでも実際には何もしません。

const char *padding = "";
for (auto iter = keywords.begin(); iter != keywords.end(); ++iter) {
    out << padding << *iter;
    padding = ", "
}
于 2010-08-16T23:05:11.210 に答える
17

一般的なアプローチの1つは、ループの前に最初のアイテムを印刷し、残りのアイテムのみをループして、残りの各アイテムの前にコンマを事前に印刷することです。

または、行の現在の状態(endlの前)を維持し、適切な場所にコンマを配置する独自のストリームを作成できる必要があります。

編集:TEDが提案するように、中間テスト済みのループを使用することもできます。次のようになります。

if(!keywords.empty())
{
    auto iter = keywords.begin();
    while(true)
    {
        out << *iter;
        ++iter;
        if(iter == keywords.end())
        {
            break;
        }
        else
        {
            out << ", ";
        }
    }
}

ループ本体を非常にシンプルに保つため、最初に「ループの前に最初のアイテムを印刷する」方法について説明しましたが、どのアプローチでも問題なく機能します。

于 2010-08-16T20:22:28.333 に答える
12

巧妙な解決策はたくさんありますが、あまりにも多くの解決策が、コンパイラにその仕事をさせずに、救いの望みを超えてコードを壊してしまいます。

明らかな解決策は、最初の繰り返しを特別なケースにすることです。

bool first = true;
for (auto const& e: sequence) {
   if (first) { first = false; } else { out << ", "; }
   out << e;
}

これは非常に単純なパターンです。

  1. ループを台無しにしません: 各要素が繰り返されることは一目瞭然です。
  2. elseブロックとループ本体には任意のステートメントを含めることができるため、セパレーターを配置したり、実際にリストを出力したりするだけではありません。

これは絶対的に最も効率的なコードではないかもしれませんが、十分に予測された単一の分岐の潜在的なパフォーマンスの低下は、std::ostream::operator<<.

于 2016-02-14T15:11:59.877 に答える
7

このようなもの?

while (iter != keywords.end())
{
 out << *iter;
 iter++;
 if (iter != keywords.end()) cout << ", ";
}
于 2010-08-16T20:23:45.907 に答える
5

Python では、次のように記述します。

print ", ".join(keywords)

では、なぜでしょうか:

template<class S, class V>
std::string
join(const S& sep, const V& v)
{
  std::ostringstream oss;
  if (!v.empty()) {
    typename V::const_iterator it = v.begin();
    oss << *it++;
    for (typename V::const_iterator e = v.end(); it != e; ++it)
      oss << sep << *it;
  }
  return oss.str();
}

そして、次のように使用します:

cout << join(", ", keywords) << endl;

上記の python の例では、" "は文字列であり、 は文字列のイテラブルでなければなりませんがkeywords、この C++ の例ではセパレーターであり、ストリーミング可能なものであれば何でもかまいません。keywords

cout << join('\n', keywords) << endl;
于 2015-09-17T07:52:35.713 に答える
5

区切り記号を (任意の言語で) 実行するための私の典型的な方法は、中間テスト済みのループを使用することです。C++ コードは次のようになります。

for (;;) {
   std::cout << *iter;
   if (++iter == keywords.end()) break;
   std::cout << ",";
}

if(注:キーワードが空の可能性がある場合は、ループの前に追加のチェックが必要です)

示されている他のソリューションのほとんどは、ループの反復ごとに余分なテスト全体を実行することになります。あなたは I/O を行っているので、それにかかる時間は大きな問題ではありませんが、私の感性を不快にさせます。

于 2010-08-16T20:49:21.067 に答える
3

これを試して:

typedef  std::vector<std::string>   Container;
typedef Container::const_iterator   CIter;
Container   data;

// Now fill the container.


// Now print the container.
// The advantage of this technique is that ther is no extra test during the loop.
// There is only one additional test !test.empty() done at the beginning.
if (!data.empty())
{
    std::cout << data[0];
    for(CIter loop = data.begin() + 1; loop != data.end(); ++loop)
    {
        std::cout << "," << *loop;
    }
}
于 2010-08-16T20:25:36.730 に答える
3

ifをループ内に配置しないようにするために、これを使用します。

vector<int> keywords = {1, 2, 3, 4, 5};

if (!keywords.empty())
{
    copy(keywords.begin(), std::prev(keywords.end()), 
         std::ostream_iterator<int> (std::cout,", "));
    std::cout << keywords.back();
}

ベクターの種類によって異なりintますが、ヘルパーで削除できます。

于 2016-08-23T07:28:07.297 に答える
2

++使用している演算子に少し問題があります。

あなたが試すことができます:

if (++iter != keywords.end())
    out << ", ";
iter--;

このように++、イテレータをと比較する前に評価されますkeywords.end()

于 2010-08-16T20:23:30.383 に答える
2

そのために小さなヘルパー クラスを使用します。

class text_separator {
public:
    text_separator(const char* sep) : sep(sep), needsep(false) {}

    // returns an empty string the first time it is called
    // returns the provided separator string every other time
    const char* operator()() {
        if (needsep)
            return sep;
        needsep = true;
        return "";
    }

    void reset() { needsep = false; }

private:
    const char* sep;
    bool needsep;
};

使用するには:

text_separator sep(", ");
for (int i = 0; i < 10; ++i)
    cout << sep() << i;
于 2011-07-11T14:37:22.163 に答える
2

回避する別の可能な解決策if

Char comma = '[';
for (const auto& element : elements) {
    std::cout.put(comma) << element;
    comma = ',';
}
std::cout.put(']');

ループで何をしているかによって異なります。

于 2013-07-22T16:15:13.887 に答える
2

以下を行う必要があります:-

 const std::vector<__int64>& a_setRequestId
 std::stringstream strStream;
 std::copy(a_setRequestId.begin(), a_setRequestId.end() -1, std::ostream_iterator<__int64>(strStream, ", "));
 strStream << a_setRequestId.back();
于 2016-12-28T22:33:44.150 に答える
1

これでうまくいくと思います

while (iter != keywords.end( ))
{

    out << *iter;
    iter++ ;
    if (iter != keywords.end( )) out << ", ";
}
于 2010-08-16T20:23:22.843 に答える
1

ブーストの使用:

std::string add_str("");
const std::string sep(",");

for_each(v.begin(), v.end(), add_str += boost::lambda::ret<std::string>(boost::lambda::_1 + sep));

そして、カンマで区切られたベクトルを含む文字列を取得します。

編集:最後のカンマを削除するには、次を発行します:

add_str = add_str.substr(0, add_str.size()-1);
于 2012-06-05T11:33:18.047 に答える
1

そうかも..

bool bFirst = true;
for (auto curr = keywords.begin();  curr != keywords.end(); ++curr) {
   std::cout << (bFirst ? "" : ", ") << *curr;
   bFirst = false;
}
于 2010-08-16T21:43:18.273 に答える
0

私はこのような簡単な解決策を採用し、すべてのイテレータで機能するはずです。

int maxele = maxele = v.size() - 1;
for ( cur = v.begin() , i = 0; i < maxele ; ++i)
{
    std::cout << *cur++ << " , ";
}
if ( maxele >= 0 )
{
  std::cout << *cur << std::endl;
}
于 2010-08-16T23:14:46.800 に答える
0

ファンクターを使用できます:

#include <functional>

string getSeparatedValues(function<bool()> condition, function<string()> output, string separator)
{
    string out;
    out += output();
    while (condition())
        out += separator + output();
    return out;
}

例:

if (!keywords.empty())
{
    auto iter = keywords.begin();
    cout << getSeparatedValues([&]() { return ++iter != keywords.end(); }, [&]() { return *iter; }, ", ") << endl;
}
于 2016-10-25T07:07:18.987 に答える
0

ループを使用しdoて、最初の繰り返しのループ条件を書き直し、短絡&&演算子と有効なストリームが であるという事実を使用できますtrue

auto iter = keywords.begin();
if ( ! keywords.empty() ) do {
    out << * iter;
} while ( ++ iter != keywords.end() && out << ", " );
out << endl;
于 2010-08-16T21:55:49.380 に答える
0

is_last_elemテスト付きの範囲ベースの for が好きです。その私見は非常に読みやすいです:

for (auto& e : range)
{
    if (!is_last_elem(e, range)) [[likely]] 
        os << e << ", ";
    else
        os << e;
}
os << std::endl;

完全なコード:

C++20:

#include <iostream>
#include <list>
#include <ranges>
#include <utility>
#include <type_traits>
#include <memory>

template <std::ranges::bidirectional_range R>
bool is_last_elem(const std::ranges::range_value_t<R>& elem, const R& range)
{
    auto last_it = range.end();
    std::advance(last_it, -1);
    return std::addressof(elem) == std::addressof(*last_it);
}

template <std::ranges::bidirectional_range R, class Stream = std::ostream>
void print(const R& range, std::ostream& os = std::cout)
{
    for (auto& e : range)
    {
        if (!is_last_elem(e, range)) [[likely]] 
            os << e << ", ";
        else
            os << e;
    }
    os << std::endl;
}

int main()
{
    std::list<int> v{1, 2, 3, 4, 5};
    print(v);
}

C++17:

#include <iostream>
#include <list>
#include <utility>
#include <type_traits>
#include <memory>

template <class Range>
using value_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<Range>()))>;

template <class Range>
bool is_last_elem(const value_type_t<Range>& elem, const Range& range)
{
    auto last_it = range.end();
    std::advance(last_it, -1);
    return std::addressof(elem) == std::addressof(*last_it);
}

template <class Range, class Stream = std::ostream>
void print(const Range& range, std::ostream& os = std::cout)
{
    for (auto& e : range)
    {
        if (!is_last_elem(e, range))
            os << e << ", ";
        else
            os << e;
    }
    os << std::endl;
}

int main()
{
    std::list<int> v{1, 2, 3, 4, 5};
    print(v);
}
于 2021-04-21T06:35:25.633 に答える