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?
Verwenden Sie boost::algorithm::join(..)
:
#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);
Siehe auch diese Frage .
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
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:
delim
muss jetzt ein const char*
und nicht eine einzelne char
sein. Keine große Sache.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
).string join(const vector<string>& vec, const char* delim)
{
stringstream res;
copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
return res.str();
}
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();
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";
}
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();
}
}
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
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 "";
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;
}
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
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]
fügen Sie einfach !! String s = "";
for (int i = 0; i < doc.size(); i++) //doc is the vector
s += doc[i];
Etwas lange Lösung, verwendet jedoch std::ostringstream
nicht und erfordert keinen Hack, um den letzten Begrenzer zu entfernen.
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()));
}
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()+ "]";
}