Comment imploser un vecteur de chaînes dans une chaîne (la manière élégante)

Je cherche la manière la plus élégante d’imploser un vecteur de chaînes dans une chaîne. Voici la solution que j’utilise maintenant:

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

Y a-t-il d’autres là-bas?

Utilisez boost::algorithm::join(..) :

 #include  ... std::ssortingng joinedSsortingng = boost::algorithm::join(elems, delim); 

Voir aussi cette question .

 std::vector ssortingngs; const char* const delim = ", "; std::ossortingngstream imploded; std::copy(ssortingngs.begin(), ssortingngs.end(), std::ostream_iterator(imploded, delim)); 

(inclure , , et )

Si vous voulez avoir une fin propre (pas de délimiteur de fin), regardez ici

Vous devez utiliser std::ossortingngstream plutôt que std::ssortingng pour générer la sortie (vous pouvez alors appeler sa méthode str() à la fin pour obtenir une chaîne, votre interface n’a donc pas besoin de changer, seulement les s temporaires).

À partir de là, vous pouvez utiliser std::ostream_iterator , comme ceci:

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

Mais cela a deux problèmes:

  1. delim doit maintenant être un const char* plutôt qu’un simple caractère. Pas grand chose
  2. 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’iterator qui n’a pas cette gêne. Cela vaut la peine de faire ce dernier si vous avez beaucoup de code qui a besoin de choses comme ça; Sinon, il serait préférable d’éviter tout le désordre (utiliser ossortingngstream mais pas ostream_iterator ).

Parce que j’aime les one-liners (ils sont très utiles pour toutes sortes de choses bizarres, comme vous le verrez à la fin), voici une solution avec std :: accumulate et C ++ 11 lambda:

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

Je trouve cette syntaxe utile avec l’opérateur de stream, où je ne veux pas avoir toutes sortes de logique étrange hors de scope de l’opération de stream, juste pour faire une simple jointure de chaîne. Considérons par exemple cette déclaration de retour de la méthode qui formate une chaîne à l’aide des opérateurs de stream (en utilisant std;):

 return (dynamic_cast(ossortingngstream() < < "List content: " << endl << std::accumulate(alist.begin(), alist.end(), std::string(), [](const std::string& a, const std::string& b) -> std::ssortingng { return a + (a.length() > 0 ? "," : "") + b; } ) < < endl << "Maybe some more stuff" << endl )).str(); 
 ssortingng join(const vector& vec, const char* delim) { ssortingngstream res; copy(vec.begin(), vec.end(), ostream_iterator(res, delim)); return res.str(); } 

Une version utilisant std::accumulate :

 #include  #include  #include  struct infix { std::ssortingng sep; infix(const std::ssortingng& sep) : sep(sep) {} std::ssortingng operator()(const std::ssortingng& lhs, const std::ssortingng& rhs) { std::ssortingng rz(lhs); if(!lhs.empty() && !rhs.empty()) rz += sep; rz += rhs; return rz; } }; int main() { std::ssortingng a[] = { "Hello", "World", "is", "a", "program" }; std::ssortingng sum = std::accumulate(a, a+5, std::ssortingng(), infix(", ")); std::cout < < sum << "\n"; } 

Voici un autre qui n’ajoute pas le délimiteur après le dernier élément:

 std::ssortingng concat_ssortingngs(const std::vector &elements, const std::ssortingng &separator) { if (!elements.empty()) { std::ssortingngstream ss; auto it = elements.cbegin(); while (true) { ss < < *it++; if (it != elements.cend()) ss << separator; else return ss.str(); } } return ""; 

Surtout avec les plus grandes collections, vous devez éviter de vérifier si vous ajoutez encore le premier élément ou ne pas vous assurer de séparateur de fin.

Donc, pour la liste vide ou mono-élément, il n’y a pas d’itération du tout.

Les plages vides sont sortingviales: retourner “”.

Un seul élément ou multi-élément peut être manipulé parfaitement en accumulate :

 auto join = [](const auto &&range, const auto separator) { if (range.empty()) return std::ssortingng(); 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 ( nécessite C ++ 14 ): http://cpp.sh/8uspd

Qu’en est-il d’une solution simple et stupide?

 std::ssortingng Ssortingng::join(const std::vector &lst, const std::ssortingng &delim) { std::ssortingng ret; for(const auto &s : lst) { if(!ret.empty()) ret += delim; ret += s; } return ret; } 

Solution légèrement longue, mais n’utilise pas std::ossortingngstream , et ne nécessite pas de hack pour supprimer le dernier délimiteur.

http://www.ideone.com/hW1M9

Et le code:

 struct appender { appender(char d, std::ssortingng& sd, int ic) : delim(d), dest(sd), count(ic) { dest.reserve(2048); } void operator()(std::ssortingng const& copy) { dest.append(copy); if (--count) dest.append(1, delim); } char delim; mutable std::ssortingng& dest; mutable int count; }; void implode(const std::vector& elems, char delim, std::ssortingng& s) { std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size())); } 

Ajoutez simplement !! Ssortingng s = “”;

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

Voici ce que j’utilise, simple et flexible

 ssortingng joinList(vector arr, ssortingng delimiter) { if (arr.empty()) return ""; ssortingng str; for (auto i : arr) str += i + delimiter; str = str.substr(0, str.size() - delimiter.size()); return str; } 

en utilisant:

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

sortie:

 a!@#bbb!@#c 

Tout d’abord, une classe de stream ossortingngstream est nécessaire ici pour faire la concaténation plusieurs fois et sauver le problème sous-jacent d’une allocation de mémoire excessive.

Code:

 ssortingng join(const vector& vec, const char* delim) { ossortingngstream oss; if(!ssortingng_vector.empty()) { copy(ssortingng_vector.begin(),ssortingng_vector.end() - 1, ostream_iterator(oss, delim.c_str())); } return oss.str(); } vector ssortingng_vector {"1", "2"}; ssortingng delim("->"); ssortingng joined_ssortingng = join(); // get "1->2" 

Explication:

en pensant, traitez oss ici comme std::cout

quand on veut écrire:

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

nous pouvons utiliser les classes STL suivantes comme aide:

ostream_iterator renvoie un stream de sortie ostream_iterator avec des délimiteurs ajoutés automatiquement chaque fois que vous utilisez < < .

par exemple,

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

wraps std:cout comme my_cout

donc à chaque fois que vous my_cout < < "string_vector[0]" ,

cela signifie std::cout < < "string_vector[0]" << "->"

Comme pour la copy(vector.begin(), vector.end(), std::out);

cela signifie std::cout < < vector[0] << vector[1] (...) << vector[end]

Essayez ceci, mais en utilisant le vecteur au lieu de la liste

 template  std::ssortingng listToSsortingng(std::list l){ std::ssortingngstream ss; for(std::list::iterator it = l.begin(); it!=l.end(); ++it){ ss < < *it; if(std::distance(it,l.end())>1) ss < < ", "; } return "[" + ss.str()+ "]"; } 

En utilisant une partie de cette réponse à une autre question, vous vous joignez à ceci, basé sur un séparateur sans virgule finale,

Usage:

 std::vector input_str = std::vector({"a", "b", "c"}); std::ssortingng result = ssortingng_join(input_str, ","); printf("%s", result.c_str()); /// a,b,c 

Code:

 std::ssortingng ssortingng_join(const std::vector& elements, const char* const separator) { switch (elements.size()) { case 0: return ""; case 1: return elements[0]; default: std::ossortingngstream os; std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator(os, separator)); os < < *elements.rbegin(); return os.str(); } }