web-dev-qa-db-ger.com

Wie man einen Vektor von Strings in einen String implodiert (der elegante Weg)

Ich suche den elegantesten Weg, einen Vektor von Strings in einen String zu implodieren. Hier ist die Lösung, die ich jetzt verwende:

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);
}

Gibt es noch andere da draußen?

60
ezpresso

Verwenden Sie boost::algorithm::join(..):

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

Siehe auch diese Frage .

116
Andre Holzner
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));

(Include <string>, <vector>, <sstream> und <iterator>)

Wenn Sie ein sauberes Ende haben möchten (kein nachlaufendes Trennzeichen), schauen Sie hier

23
sehe

Sie sollten std::ostringstream anstelle von std::string verwenden, um die Ausgabe zu erstellen (dann können Sie die str()-Methode am Ende aufrufen, um eine Zeichenfolge zu erhalten, sodass Ihre Schnittstelle nicht geändert werden muss, nur die temporäre s).

Von dort aus können Sie wie folgt zu std::ostream_iterator wechseln:

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

Dies hat jedoch zwei Probleme:

  1. delim muss jetzt ein const char* und nicht eine einzelne char sein. Keine große Sache.
  2. std::ostream_iterator schreibt das Trennzeichen nach jedem einzelnen Element, einschließlich des letzten. Sie müssen entweder die letzte am Ende löschen oder eine eigene Version des Iterators schreiben, die diesen Ärger nicht hat. Letzteres wäre es wert, wenn Sie viel Code haben, der solche Dinge benötigt. Andernfalls kann das ganze Durcheinander am besten vermieden werden (d. h. ostringstream verwenden, jedoch nicht ostream_iterator).
21
John Zwinck
string join(const vector<string>& vec, const char* delim)
{
    stringstream res;
    copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
    return res.str();
}
9
BlackMamba

Weil ich Einzeiler liebe (sie sind sehr nützlich für alle Arten von seltsamen Sachen, wie Sie am Ende sehen werden), hier eine Lösung mit std :: collectulate und C++ 11-Lambda:

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

Ich finde diese Syntax für den Stream-Operator nützlich, wo ich nicht alle Arten von seltsamer Logik außerhalb des Stream-Vorgangs haben möchte, nur um einen einfachen String-Join auszuführen. Betrachten Sie zum Beispiel diese return-Anweisung von einer Methode, die eine Zeichenfolge mithilfe von Stream-Operatoren (mit std;) formatiert:

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();
9
Guss

Eine Version, die std::accumulate verwendet:

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

Wenn Sie einen Teil dieser answer zu einer anderen Frage verwenden, erhalten Sie eine Verknüpfung, die auf einem Trennzeichen ohne nachfolgendes Komma basiert.

Verwendungszweck:

std::vector<std::string> input_str = std::vector<std::string>({"a", "b", "c"});
std::string result = string_join(input_str, ",");
printf("%s", result.c_str());
/// a,b,c

Code:

std::string string_join(const std::vector<std::string>& elements, const char* const separator)
{
    switch (elements.size())
    {
        case 0:
            return "";
        case 1:
            return elements[0];
        default:
            std::ostringstream os;
            std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator<std::string>(os, separator));
            os << *elements.rbegin();
            return os.str();
    }
}
2
TankorSmash

Insbesondere bei größeren Kollektionen möchten Sie vermeiden, dass Sie prüfen, ob Sie noch das erste Element hinzufügen oder nicht, um kein nachträgliches Trennzeichen zu gewährleisten.

Für die leere oder Einzelelementliste gibt es also überhaupt keine Iteration.

Leere Bereiche sind trivial: Rückgabe "".

Einzel- oder Mehrfachelemente können mit accumulate perfekt behandelt werden:

auto join = [](const auto &&range, const auto separator) {
    if (range.empty()) return std::string();

    return std::accumulate(
         next(begin(range)), // there is at least 1 element, so OK.
         end(range),

         range[0], // the initial value

         [&separator](auto result, const auto &value) {
             return result + separator + value;
         });
};

Laufendes Beispiel (required C++ 14): http://cpp.sh/8uspd

2
xtofl

Hier ist eine andere, die das Trennzeichen nach dem letzten Element nicht hinzufügt:

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 "";
2
Darien Pardinas

was ist mit einer einfachen blöden Lösung?

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;
}
2
user3545770

Hier ist was ich benutze, einfach und flexibel

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

mit:

string a = joinList({ "a", "bbb", "c" }, "[email protected]#");

ausgabe:

[email protected]#[email protected]#c
1
user1438233

Erstens wird hier eine Stream-Klasse ostringstream benötigt, um die Verkettung für viele Male durchzuführen und die zugrunde liegenden Probleme einer übermäßigen Speicherzuweisung zu vermeiden.

Code:

string join(const vector<string>& vec, const char* delim)
{
    ostringstream oss;
    if(!string_vector.empty()) {
        copy(string_vector.begin(),string_vector.end() - 1, ostream_iterator<string>(oss, delim.c_str()));
    }
    return oss.str();
}

vector<string> string_vector {"1", "2"};
string delim("->");
string joined_string = join();  // get "1->2"

Erläuterung:

wenn Sie darüber nachdenken, behandeln Sie oss hier als std::cout

wenn wir schreiben wollen:

std::cout << string_vector[0] << "->" << string_vector[1] << "->",

wir können die folgenden STL-Klassen als Hilfe verwenden:

ostream_iterator Gibt einen umbrochenen Ausgabestream mit Begrenzern zurück, die bei jeder Verwendung von << Automatisch angehängt werden.

zum Beispiel,

ostream my_cout = ostream_iterator<string>(std::cout, "->")

hüllt std:cout in my_cout

also jedes Mal, wenn Sie my_cout << "string_vector[0]",

es bedeutet std::cout << "string_vector[0]" << "->"

Wie für copy(vector.begin(), vector.end(), std::out);

es bedeutet std::cout << vector[0] << vector[1] (...) << vector[end]

1
Shihao Xu

fügen Sie einfach !! String s = "";

for (int i = 0; i < doc.size(); i++)   //doc is the vector
    s += doc[i];
1
cleanplay

Etwas lange Lösung, verwendet jedoch std::ostringstream nicht und erfordert keinen Hack, um den letzten Begrenzer zu entfernen.

http://www.ideone.com/hW1M9

Und der Code:

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()));
}
1
Nim

versuchen Sie dies, verwenden Sie jedoch Vektor anstelle von Liste

template <class T>
std::string listToString(std::list<T> l){
    std::stringstream ss;
    for(std::list<int>::iterator it = l.begin(); it!=l.end(); ++it){
        ss << *it;
        if(std::distance(it,l.end())>1)
            ss << ", ";
    }
    return "[" + ss.str()+ "]";
}
0
D. Pérez