Je cherche le moyen le plus élégant d'imploser un vecteur de chaînes dans une chaîne. Voici la solution que j'utilise maintenant:
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);
}
Y en at-il d'autres?
Utilisez boost::algorithm::join(..)
:
#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);
Voir aussi cette question .
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));
(inclure <string>
, <vector>
, <sstream>
et <iterator>
)
Si vous voulez avoir une extrémité propre (pas de délimiteur final), regardez ici
Vous devez utiliser std::ostringstream
plutôt que std::string
pour générer la sortie (vous pouvez ensuite appeler sa méthode str()
à la fin pour obtenir une chaîne; votre interface n'a donc pas besoin de changer, seule la variable temporaire s
).
À partir de là, vous pouvez utiliser std::ostream_iterator
comme suit:
copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim));
Mais cela a deux problèmes:
delim
doit maintenant être un const char*
, plutôt qu'un seul char
. Pas grave.std::ostream_iterator
écrit le délimiteur après chaque élément, y compris le dernier. Donc vous devez soit effacer le dernier à la fin, soit écrire votre propre version de l’itérateur qui n’a pas cette gêne. Ce serait la peine de faire cela si vous avez beaucoup de code qui a besoin de choses comme ça; sinon, il serait préférable d’éviter tout le bazar (utilisez ostringstream
mais pas 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();
}
Parce que j'aime les one-liners (ils sont très utiles pour toutes sortes de choses étranges, comme vous le verrez à la fin), voici une solution utilisant std :: accumulate et 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;
} )
Je trouve cette syntaxe utile avec l'opérateur de flux, où je ne souhaite pas avoir toutes sortes de logique étrange hors de portée de l'opération de flux, juste pour faire une simple jointure de chaîne. Considérons par exemple cette instruction return de la méthode qui formate une chaîne à l'aide d'opérateurs de flux (à l'aide de std;):
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();
Une version qui utilise 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";
}
Utiliser une partie de answer pour une autre question vous donne une jointure ceci, basée sur un séparateur sans virgule de fin,
Usage:
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();
}
}
Surtout avec les plus grandes collections, vous voulez éviter de vérifier si vous ajoutez toujours le premier élément ou non pour vous assurer qu'il n'y a pas de séparateur final ...
Donc, pour la liste vide ou à un seul élément, il n'y a pas d'itération du tout.
Les plages vides sont triviales: retour "".
Un seul élément ou plusieurs éléments peuvent être parfaitement gérés par 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;
});
};
Exemple en cours d'exécution (require C++ 14): http://cpp.sh/8uspd
Voici un autre qui n'ajoute pas le délimiteur après le dernier élément:
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 "";
qu'en est-il de la solution simple et stupide?
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;
}
Voici ce que j'utilise, simple et flexible
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;
}
en utilisant:
string a = joinList({ "a", "bbb", "c" }, "!@#");
sortie:
a!@#bbb!@#c
ajoutez simplement !! String s = "";
for (int i = 0; i < doc.size(); i++) //doc is the vector
s += doc[i];
Solution légèrement longue, mais n'utilise pas std::ostringstream
, et ne nécessite pas de piratage pour supprimer le dernier délimiteur.
Et le 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()));
}
essayez ceci, mais utilisez vector au lieu de list
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()+ "]";
}