Ich suche nach der elegantesten Möglichkeit, einen String-Vektor in einen String zu implodieren. Unten 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 da draußen noch andere?
Antworten:
Verwendung
boost::algorithm::join(..)
:#include <boost/algorithm/string/join.hpp> ... std::string joinedString = boost::algorithm::join(elems, delim);
Siehe auch diese Frage .
quelle
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));
(gehört
<string>
,<vector>
,<sstream>
und<iterator>
)Wenn Sie ein sauberes Ende haben möchten (kein abschließendes Trennzeichen), schauen Sie hier
quelle
std::ostream_iterator
Konstruktor am Ende des Streams.Sie sollten die Ausgabe nicht verwenden,
std::ostringstream
sondernstd::string
erstellen (dann können Sie diestr()
Methode am Ende aufrufen , um eine Zeichenfolge abzurufen, sodass sich Ihre Schnittstelle nicht ändern muss, sondern nur die temporäres
).Von dort aus können Sie wie folgt zur Verwendung wechseln
std::ostream_iterator
:copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim));
Dies hat jedoch zwei Probleme:
delim
Jetzt muss esconst char*
eher eine als eine einzige seinchar
. Keine große Sache.std::ostream_iterator
schreibt das Trennzeichen nach jedem einzelnen Element, einschließlich des letzten. Sie müssten also entweder den letzten am Ende löschen oder eine eigene Version des Iterators schreiben, die diesen Ärger nicht hat. Letzteres lohnt sich, wenn Sie viel Code haben, der solche Dinge benötigt. Andernfalls könnte das ganze Durcheinander am besten vermieden werden (dh verwenden,ostringstream
aber nichtostream_iterator
).quelle
Da ich Einzeiler liebe (sie sind sehr nützlich für alle Arten von seltsamen Dingen, wie Sie am Ende sehen werden), gibt es hier eine Lösung mit std :: accumulate 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 nützlich für Stream-Operatoren, bei denen ich nicht alle Arten von seltsamer Logik außerhalb des Bereichs der Stream-Operation haben möchte, nur um einen einfachen String-Join durchzuführen. Betrachten Sie zum Beispiel diese return-Anweisung von einer Methode, die eine Zeichenfolge mithilfe von Stream-Operatoren (mithilfe von 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();
Aktualisieren:
Wie von @plexando in den Kommentaren hervorgehoben, weist der obige Code ein Fehlverhalten auf, wenn das Array mit leeren Zeichenfolgen beginnt, da bei der Prüfung auf "erster Lauf" frühere Läufe fehlen, die zu keinen zusätzlichen Zeichen geführt haben, und außerdem - Es ist seltsam, bei allen Läufen nach "wird zuerst ausgeführt" zu suchen (dh der Code ist unteroptimiert).
Die Lösung für diese beiden Probleme ist einfach, wenn wir wissen, dass die Liste mindestens ein Element enthält. OTOH, wenn wir sicher wissen, dass die Liste nicht mindestens ein Element enthält, können wir den Lauf noch weiter verkürzen.
Ich denke, der resultierende Code ist nicht so hübsch, also füge ich ihn hier als The Correct Solution hinzu , aber ich denke, die obige Diskussion hat immer noch Merrit:
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; });
Anmerkungen:
alist[0]
für Vektoren.[](auto&& a, auto&& b) -> auto& { a += ','; a += b; return a; })
das (bei GCC 10) die Leistung um mehr als x10 verbessert. Vielen Dank an @Deduplicator für den Vorschlag. Ich versuche immer noch herauszufinden, was hier los ist.quelle
accumulate
für Strings verwenden. Die meisten anderen Antworten sind O (n), aberaccumulate
O (n ^ 2), da vor dem Anhängen jedes Elements eine temporäre Kopie des Akkumulators erstellt wird. Und nein, Bewegungssemantik hilft nicht.T
eine Überlastung vorliegtoperator+
(wie dies derstring
Fall ist) oder wenn Sie Ihren eigenen Funktor bereitstellen, sind alle Wetten ungültig. Obwohl ich vielleicht hastig gesagt habe, dass die Bewegungssemantik nicht hilft, lösen sie das Problem in den beiden von mir überprüften Implementierungen nicht. Siehe meine Antworten auf ähnliche Fragen .implode
Beispiel des OP ) machen das Richtige. Sie sind O (n), auch wenn siereserve
die Zeichenfolge nicht aufrufen . Nur die Lösung mit akkumulieren ist O (n ^ 2). Kein C-Code erforderlich.Was ist mit einer einfachen dummen 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; }
quelle
string join(const vector<string>& vec, const char* delim) { stringstream res; copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim)); return res.str(); }
quelle
Ich verwende gerne diesen Einzeiler (kein nachfolgendes Trennzeichen):
std::accumulate( std::next(elems.begin()), elems.end(), elems[0], [](std::string a, std::string b) { return a + delimiter + b; } );
quelle
Insbesondere bei größeren Sammlungen möchten Sie vermeiden, dass Sie überprüfen müssen, ob Sie noch das erste Element hinzufügen oder nicht, um sicherzustellen, dass kein nachfolgendes Trennzeichen vorhanden ist.
Für die leere Liste oder die Liste mit einzelnen Elementen gibt es also überhaupt keine Iteration.
Leere Bereiche sind trivial: return "".
Einzel- oder Mehrfachelemente können perfekt gehandhabt werden durch
accumulate
: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; }); };
Ausführendes Beispiel ( C ++ 14 erforderlich ): http://cpp.sh/8uspd
quelle
Eine Version, die verwendet
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"; }
quelle
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 "";
quelle
Wenn Sie einen Teil dieser Antwort auf eine andere Frage verwenden, erhalten Sie eine Verknüpfung, basierend auf einem Trennzeichen ohne nachfolgendes Komma.
Verwendung:
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(); } }
quelle
Folgendes verwende ich, 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" }, "!@#");
Ausgabe:
a!@#bbb!@#c
quelle
Etwas lange Lösung, wird aber nicht verwendet
std::ostringstream
und erfordert keinen Hack, um das letzte Trennzeichen 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())); }
quelle
Eine mögliche Lösung mit ternärem Operator
?:
.std::string join(const std::vector<std::string> & v, const std::string & delimiter = ", ") { std::string result; for (size_t i = 0; i < v.size(); ++i) { result += (i ? delimiter : "") + v[i]; } return result; }
join({"2", "4", "5"})
werde dir geben2, 4, 5
.quelle
einfach hinzufügen !! String s = "";
for (int i = 0; i < doc.size(); i++) //doc is the vector s += doc[i];
quelle
Versuchen Sie dies, aber verwenden Sie den Vektor anstelle der 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()+ "]"; }
quelle