96

文字列のベクトルを文字列に内破する最もエレガントな方法を探しています。以下は、私が現在使用しているソリューションです。

static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
    for (std::vector<std::string>::const_iterator ii = elems.begin(); ii != elems.end(); ++ii)
    {
        s += (*ii);
        if ( ii + 1 != elems.end() ) {
            s += delim;
        }
    }

    return s;
}

static std::string implode(const std::vector<std::string>& elems, char delim)
{
    std::string s;
    return implode(elems, delim, s);
}

そこに他の人はいますか?

4

18 に答える 18

131

使用boost::algorithm::join(..):

#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);

この質問も参照してください。

于 2011-06-13T17:52:00.933 に答える
36
std::vector<std::string> strings;

const char* const delim = ", ";

std::ostringstream imploded;
std::copy(strings.begin(), strings.end(),
           std::ostream_iterator<std::string>(imploded, delim));

( <string><vector><sstream>および を含む<iterator>)

きれいな終わり(末尾の区切り文字なし)が必要な場合は、こちらをご覧ください

于 2011-04-16T19:36:44.197 に答える
26

std::ostringstream出力を構築するのではなく、を使用する必要がstd::stringあります (その後、最後にそのメソッドを呼び出してstr()文字列を取得できるため、インターフェイスを変更する必要はなく、一時的なs.

そこから、次std::ostream_iteratorのように using に変更できます。

copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

しかし、これには 2 つの問題があります。

  1. delimconst char*単一の ではなく である必要がありますchar。大きな問題ではない。
  2. std::ostream_iterator最後のものを含め、すべての単一要素の後に区切り文字を書き込みます。したがって、最後の最後のものを消去するか、この煩わしさのない独自のバージョンのイテレータを作成する必要があります。このようなことを必要とするコードがたくさんある場合は、後者を実行する価値があります。そうしないと、全体の混乱を避けるのが最善かもしれません (つまり、 を使用しますが、使用ostringstreamしないでくださいostream_iterator)。
于 2011-04-16T19:33:33.633 に答える
16

私はワンライナーが大好きなので (最後に説明するように、ワンライナーはあらゆる種類の奇妙なものに非常に役立ちます)、std::accumulate と C++11 ラムダを使用したソリューションを次に示します。

std::accumulate(alist.begin(), alist.end(), std::string(), 
    [](const std::string& a, const std::string& b) -> std::string { 
        return a + (a.length() > 0 ? "," : "") + b; 
    } )

この構文は、単純な文字列結合を行うためだけに、ストリーム操作の範囲外にあらゆる種類の奇妙なロジックを持ちたくないストリーム演算子で役立ちます。たとえば、ストリーム演算子 (std; を使用) を使用して文字列をフォーマットするメソッドからの次の return ステートメントを考えてみましょう。

return (dynamic_cast<ostringstream&>(ostringstream()
    << "List content: " << endl
    << std::accumulate(alist.begin(), alist.end(), std::string(), 
        [](const std::string& a, const std::string& b) -> std::string { 
            return a + (a.length() > 0 ? "," : "") + b; 
        } ) << endl
    << "Maybe some more stuff" << endl
    )).str();

アップデート:

コメントで @plexando が指摘したように、配列が空の文字列で始まる場合、上記のコードは誤動作の影響を受けます。これは、「最初の実行」のチェックで、追加の文字が発生しなかった以前の実行が欠落しているためです。また、-すべての実行で "is first run" のチェックを実行するのは奇妙です (つまり、コードは最適化されていません)。

リストに少なくとも 1 つの要素があるという事実がわかっている場合、これらの問題の両方の解決策は簡単です。OTOH、リストに少なくとも1つの要素がないことがわかっている場合は、実行をさらに短縮できます。

結果のコードはそれほどきれいではないと思うので、正しい解決策としてここに追加しますが、上記の議論にはまだメリットがあると思います。

alist.empty() ? "" : /* leave early if there are no items in the list */
  std::accumulate( /* otherwise, accumulate */
    ++alist.begin(), alist.end(), /* the range 2nd to after-last */
    *alist.begin(), /* and start accumulating with the first item */
    [](auto& a, auto& b) { return a + "," + b; });

ノート:

  • 最初の要素への直接アクセスをサポートするコンテナーの場合は、代わりにそれalist[0]を 3 番目の引数 (ベクトル) に使用することをお勧めします。
  • コメントとチャットでの議論によると、ラムダはまだいくつかのコピーを行っています。これは、代わりにこの(あまりきれいではない)ラムダを使用することで最小限に抑えることができます。これにより、[](auto&& a, auto&& b) -> auto& { a += ','; a += b; return a; })(GCC 10で)パフォーマンスが10倍以上向上します。提案をしてくれた @Deduplicator に感謝します。私はまだここで何が起こっているのかを理解しようとしています。
于 2012-08-28T08:38:07.853 に答える
15

単純な愚かな解決策はどうですか?

std::string String::join(const std::vector<std::string> &lst, const std::string &delim)
{
    std::string ret;
    for(const auto &s : lst) {
        if(!ret.empty())
            ret += delim;
        ret += s;
    }
    return ret;
}
于 2018-01-18T12:57:48.730 に答える
3

を使用するバージョンstd::accumulate:

#include <numeric>
#include <iostream>
#include <string>

struct infix {
  std::string sep;
  infix(const std::string& sep) : sep(sep) {}
  std::string operator()(const std::string& lhs, const std::string& rhs) {
    std::string rz(lhs);
    if(!lhs.empty() && !rhs.empty())
      rz += sep;
    rz += rhs;
    return rz;
  }
};

int main() {
  std::string a[] = { "Hello", "World", "is", "a", "program" };
  std::string sum = std::accumulate(a, a+5, std::string(), infix(", "));
  std::cout << sum << "\n";
}
于 2011-04-18T19:37:08.903 に答える
2

最後の要素の後に区切り文字を追加しない別の例を次に示します。

std::string concat_strings(const std::vector<std::string> &elements,
                           const std::string &separator)
{       
    if (!elements.empty())
    {
        std::stringstream ss;
        auto it = elements.cbegin();
        while (true)
        {
            ss << *it++;
            if (it != elements.cend())
                ss << separator;
            else
                return ss.str();
        }       
    }
    return "";
于 2014-09-02T23:18:56.333 に答える
1

これが私が使用するもので、シンプルで柔軟です

string joinList(vector<string> arr, string delimiter)
{
    if (arr.empty()) return "";

    string str;
    for (auto i : arr)
        str += i + delimiter;
    str = str.substr(0, str.size() - delimiter.size());
    return str;
}

使用:

string a = joinList({ "a", "bbb", "c" }, "!@#");

出力:

a!@#bbb!@#c
于 2016-03-29T09:51:29.047 に答える
0

少し長いソリューションですが、を使用std::ostringstreamせず、最後の区切り文字を削除するためのハックを必要としません。

http://www.ideone.com/hW1M9

そしてコード:

struct appender
{
  appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic)
  {
    dest.reserve(2048);
  }

  void operator()(std::string const& copy)
  {
    dest.append(copy);
    if (--count)
      dest.append(1, delim);
  }

  char delim;
  mutable std::string& dest;
  mutable int count;
};

void implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
  std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size()));
}
于 2011-04-16T20:16:25.220 に答える