Convertir un vecteur <int> en chaîne

92

J'ai un vector<int>conteneur qui a des entiers (par exemple {1,2,3,4}) et je voudrais convertir en une chaîne de la forme

"1,2,3,4"

Quelle est la manière la plus propre de faire cela en C ++? En Python, voici comment je le ferais:

>>> array = [1,2,3,4]
>>> ",".join(map(str,array))
'1,2,3,4'
DR
la source
1
Étroitement lié: stackoverflow.com/questions/4850473/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

95

Certainement pas aussi élégant que Python, mais rien n'est aussi élégant que Python en C ++.

Vous pouvez utiliser un stringstream...

#include <sstream>
//...

std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i)
{
  if(i != 0)
    ss << ",";
  ss << v[i];
}
std::string s = ss.str();

Vous pouvez également utiliser à la std::for_eachplace.

Brian R. Bondy
la source
Je pense que vous voulez dire array.size () pas v.size (), non?
Mark Elliot
1
ya quelque soit le nom du vecteur.
Brian R. Bondy
2
Cela devrait être std::string s = ss.str(). Si vous voulez un const char*, utilisez s.c_str(). (Notez que, bien que syntaxiquement correct, ss.str().c_str()vous donnera un const char*qui pointe vers un temporaire qui cessera d'exister à la fin de l'expression complète. Cela fait mal.)
sbi
1
pourquoi ne pas simplement utiliser string.append?
Baiyan Huang
12
la réponse est incomplète sans#include <sstream>
renadeen
43

En utilisant std :: for_each et lambda, vous pouvez faire quelque chose d'intéressant.

#include <iostream>
#include <sstream>

int main()
{
     int  array[] = {1,2,3,4};
     std::for_each(std::begin(array), std::end(array),
                   [&std::cout, sep=' '](int x) mutable {
                       out << sep << x; sep=',';
                   });
}

Voir cette question pour un petit cours que j'ai écrit. Cela n'imprimera pas la virgule de fin. Aussi, si nous supposons que C ++ 14 continuera à nous donner des équivalents basés sur des plages d'algorithmes comme celui-ci:

namespace std {
   // I am assuming something like this in the C++14 standard
   // I have no idea if this is correct but it should be trivial to write if it  does not appear.
   template<typename C, typename I>
   void copy(C const& container, I outputIter) {copy(begin(container), end(container), outputIter);}
}
using POI = PrefexOutputIterator;   
int main()
{
     int  array[] = {1,2,3,4};
     std::copy(array, POI(std::cout, ","));
  // ",".join(map(str,array))               // closer
}
Martin York
la source
12
Je pense que ce n'est pas tout à fait équivalent à la jointure de Python - il insérera un "," supplémentaire à la fin.
1800 INFORMATION
2
Pas équivalent mais tout aussi élégant (en fait je pense plus mais ce n'est qu'une opinion).
Martin York
20
Évidemment, l'élégance est subjective. Donc, si vous et deux autres personnes préférez un code plus long, plus répétitif qui ne fonctionne pas, alors c'est plus élégant ;-p
Steve Jessop
1
Vous pouvez ignorer la virgule finale à l'aide de la fonction membre string :: substr. Attribuez la sous-chaîne de 0 à n-1 à votre variable de résultat.
Dan
@SteveJessop: Mieux?
Martin York
24

Vous pouvez utiliser std :: accumulate. Prenons l'exemple suivant

if (v.empty() 
    return std::string();
std::string s = std::accumulate(v.begin()+1, v.end(), std::to_string(v[0]),
                     [](const std::string& a, int b){
                           return a + ',' + std::to_string(b);
                     });
capone
la source
','devrait être","
Matt
2
@Matt La stringclasse a une surcharge pour l' +opérateur qui peut également accepter des caractères. Alors ','c'est très bien.
Pavan Manjunath
19

Une autre alternative est l'utilisation de std::copyet de la ostream_iteratorclasse:

#include <iterator>  // ostream_iterator
#include <sstream>   // ostringstream
#include <algorithm> // copy

std::ostringstream stream;
std::copy(array.begin(), array.end(), std::ostream_iterator<>(stream));
std::string s=stream.str();
s.erase(s.length()-1);

Pas aussi bien que Python. Pour cela, j'ai créé une joinfonction:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  for (A it=begin;
       it!=end;
       it++)
  {
    if (!result.empty())
      result.append(t);
    result.append(*it);
  }
  return result;
}

Ensuite, utilisé comme ceci:

std::string s=join(array.begin(), array.end(), std::string(","));

Vous pourriez vous demander pourquoi je suis passé dans les itérateurs. Eh bien, en fait, je voulais inverser le tableau, alors je l'ai utilisé comme ceci:

std::string s=join(array.rbegin(), array.rend(), std::string(","));

Idéalement, je voudrais créer un modèle au point où il peut déduire le type char et utiliser des flux de chaînes, mais je ne pouvais pas encore le comprendre.

1800 INFORMATIONS
la source
Comme ce serait trop pour un commentaire, j'ai posté une réponse ( stackoverflow.com/questions/1430757/1432040#1432040 ) qui tente de résoudre l'énigme donnée dans votre dernière phrase.
sbi
Votre joinfonction peut également être utilisée avec des vecteurs? Pouvez-vous donner un exemple, je suis nouveau en C ++.
Noitidart
Pouvez-vous changer l'itérateur en un pré-incrément dans la réponse?
Millie Smith
14

Avec Boost et C ++ 11, cela pourrait être réalisé comme ceci:

auto array = {1,2,3,4};
join(array | transformed(tostr), ",");

Enfin presque. Voici l'exemple complet:

#include <array>
#include <iostream>

#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

int main() {
    using boost::algorithm::join;
    using boost::adaptors::transformed;
    auto tostr = static_cast<std::string(*)(int)>(std::to_string);

    auto array = {1,2,3,4};
    std::cout << join(array | transformed(tostr), ",") << std::endl;

    return 0;
}

Crédit au prétorien .

Vous pouvez gérer n'importe quel type de valeur comme ceci:

template<class Container>
std::string join(Container const & container, std::string delimiter) {
  using boost::algorithm::join;
  using boost::adaptors::transformed;
  using value_type = typename Container::value_type;

  auto tostr = static_cast<std::string(*)(value_type)>(std::to_string);
  return join(container | transformed(tostr), delimiter);
};
Arekolek
la source
11

Ceci est juste une tentative de résoudre l'énigme donnée par la remarque de 1800 INFORMATION sur sa deuxième solution manquant de généricité, pas une tentative de répondre à la question:

template <class Str, class It>
Str join(It begin, const It end, const Str &sep)
{
  typedef typename Str::value_type     char_type;
  typedef typename Str::traits_type    traits_type;
  typedef typename Str::allocator_type allocator_type;
  typedef std::basic_ostringstream<char_type,traits_type,allocator_type>
                                       ostringstream_type;
  ostringstream_type result;

  if(begin!=end)
    result << *begin++;
  while(begin!=end) {
    result << sep;
    result << *begin++;
  }
  return result.str();
}

Fonctionne sur ma machine (TM).

sbi
la source
Visual Studio 2013 est très confus par les typedefs. Non pas que vous auriez pu le savoir en 2009.
Grault
@Jes: Cela fait maintenant 5 minutes que je regarde ça, mais je n'ai pas compris sur quoi VS pourrait trébucher. Peux-tu être plus précis?
sbi
Je suis désolé, je pense avoir tenté une jointure d'objets sans << surcharges, ce qui, je me rends compte maintenant, est inapproprié pour votre code. Je ne peux pas empêcher votre code de se compiler avec un vecteur de chaînes. Par ailleurs, VS 2013 Community est à la fois gratuit et riche en fonctionnalités, contrairement aux versions «Express».
Grault
@Jes: Cela devrait fonctionner avec tout type qui peut être diffusé (c'est-à-dire operator<<surchargé). Bien sûr, un type sans operator<<peut provoquer des messages d'erreur très déroutants.
sbi
Malheureusement, cela ne compile pas: join(v.begin(), v.end(), ","). La déduction d'argument de modèle ne produit pas le bon résultat si l' separgument est une chaîne littérale. Ma tentative de solution à ce problème . Fournit également une surcharge basée sur la plage plus moderne.
zett42 du
7

Beaucoup de modèles / idées. Le mien n'est pas aussi générique ou efficace, mais j'ai juste eu le même problème et je voulais ajouter cela au mix comme quelque chose de court et de doux. Il gagne sur le plus petit nombre de lignes ... :)

std::stringstream joinedValues;
for (auto value: array)
{
    joinedValues << value << ",";
}
//Strip off the trailing comma
std::string result = joinedValues.str().substr(0,joinedValues.str().size()-1);
Joe Schneider
la source
1
Merci pour le code simpliste. Vous voudrez peut-être le changer en "auto &" pour éviter les copies supplémentaires et obtenir des gains de performances faciles.
Millie Smith
Au lieu d'utiliser substr(...), utilisez pop_back()pour supprimer le dernier caractère, devient alors beaucoup plus clair et propre.
ifyalciner
4

Si vous voulez faire std::cout << join(myVector, ",") << std::endl;, vous pouvez faire quelque chose comme:

template <typename C, typename T> class MyJoiner
{
    C &c;
    T &s;
    MyJoiner(C &&container, T&& sep) : c(std::forward<C>(container)), s(std::forward<T>(sep)) {}
public:
    template<typename C, typename T> friend std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj);
    template<typename C, typename T> friend MyJoiner<C, T> join(C &&container, T&& sep);
};

template<typename C, typename T> std::ostream& operator<<(std::ostream &o, MyJoiner<C, T> const &mj)
{
    auto i = mj.c.begin();
    if (i != mj.c.end())
    {
        o << *i++;
        while (i != mj.c.end())
        {
            o << mj.s << *i++;
        }
    }

    return o;
}

template<typename C, typename T> MyJoiner<C, T> join(C &&container, T&& sep)
{
    return MyJoiner<C, T>(std::forward<C>(container), std::forward<T>(sep));
}

Notez que cette solution effectue la jointure directement dans le flux de sortie plutôt que de créer un tampon secondaire et fonctionnera avec tous les types qui ont un opérateur << sur un ostream.

Cela fonctionne également en cas d' boost::algorithm::join()échec, lorsque vous avez un vector<char*>fichier au lieu d'un vector<string>.

mheyman
la source
4
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);
chenfy27
la source
7
Bienvenue dans Stack Overflow! Bien que ce code puisse résoudre le problème, il est préférable d'ajouter une élaboration et d'expliquer comment cela fonctionne pour les personnes qui pourraient ne pas comprendre ce morceau de code.
paper1111
1
La meilleure réponse actuelle n'est pas beaucoup plus élaborée, et c'est la réponse la plus petite / la plus propre. Pas aussi efficace que std::stringstreampour les grands tableaux car stringstreamsera capable d'allouer de la mémoire de manière optimiste, conduisant à des performances O (n.log (n)) au lieu de O (n²) pour un tableau de taille npour cette réponse. Peut également stringstreamne pas créer de chaînes temporaires pour to_string(i).
aberaud le
2

J'aime la réponse de 1800. Cependant, je déplacerais la première itération hors de la boucle car le résultat de l'instruction if ne change qu'une fois après la première itération

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  A it = begin;
  if (it != end) 
  {
   result.append(*it);
   ++it;
  }

  for( ;
       it!=end;
       ++it)
  {
    result.append(t);
    result.append(*it);
  }
  return result;
}

Cela peut bien sûr être réduit à moins d'instructions si vous le souhaitez:

template <class T, class A>
T join(const A &begin, const A &end, const T &t)
{
  T result;
  A it = begin;
  if (it != end) 
   result.append(*it++);

  for( ; it!=end; ++it)
   result.append(t).append(*it);
  return result;
}
iain
la source
Vous ne devez pas utiliser la post-incrémentation pour les types d'itérateur inconnus. Cela pourrait coûter cher. (Bien sûr, lorsqu'il s'agit de chaînes, cela ne fera peut-être pas une grande différence. Mais une fois que vous aurez pris l'habitude ...)
sbi
L'incrémentation de la publication convient tant que vous utilisez la valeur temporaire renvoyée. par exemple "result.append (* it); ++ it;" est presque toujours aussi cher que "result.append (* it ++);" le second a une copie supplémentaire de l'itérateur.
iain le
Oups, je viens de repérer l'incrément de publication dans la boucle for. erreur de copie et de collage. J'ai corrigé le message.
iain le
1
@Ian: Quand j'ai enseigné le C ++, j'ai demandé à mes étudiants de l'utiliser ++isauf là où ils en avaient vraiment besoin, i++car c'était la seule façon pour eux de ne pas oublier cela quand cela faisait une différence. (C'était la même chose avec moi, BTW.) Ils avaient déjà appris Java, où toutes sortes de C-ismes sont en vogue et cela leur a pris quelques mois (1 conférence + travail de laboratoire par semaine), mais à la fin la plupart des ils ont appris l'habitude d'utiliser le pré-incrément.
sbi le
1
@sbi: d'accord que j'ai toujours par défaut le pré-incrémentation aussi, le post-incrémentation escroc est venu de copier quelqu'un d'autre pour la boucle et de la changer. Dans ma première réponse, je pensais que vous vous inquiétiez de "result.append (* it ++)" et non de la boucle for. J'étais un peu gêné de voir l'incrémentation du message dans la boucle. Certaines personnes semblent suivre le conseil de ne pas utiliser l'incrémentation de poste trop loin et de ne jamais l'utiliser ou la modifier même si cela est approprié. Cependant, je sais maintenant que vous n'entrez pas dans cette catégorie.
iain
2

Il existe des tentatives intéressantes pour apporter une solution élégante au problème. J'ai eu l'idée d'utiliser des flux basés sur des modèles pour répondre efficacement au dilemme initial du PO. Bien qu'il s'agisse d'un ancien article, j'espère que les futurs utilisateurs qui tomberont dessus trouveront ma solution bénéfique.

Premièrement, certaines réponses (y compris la réponse acceptée) ne favorisent pas la réutilisation. Puisque C ++ ne fournit pas une manière élégante de joindre des chaînes dans la bibliothèque standard (que j'ai vue), il devient important d'en créer une qui soit flexible et réutilisable. Voici ma chance:

// Replace with your namespace //
namespace my {
    // Templated join which can be used on any combination of streams, iterators and base types //
    template <typename TStream, typename TIter, typename TSeperator>
    TStream& join(TStream& stream, TIter begin, TIter end, TSeperator seperator) {
        // A flag which, when true, has next iteration prepend our seperator to the stream //
        bool sep = false;                       
        // Begin iterating through our list //
        for (TIter i = begin; i != end; ++i) {
            // If we need to prepend a seperator, do it //
            if (sep) stream << seperator;
            // Stream the next value held by our iterator //
            stream << *i;
            // Flag that next loops needs a seperator //
            sep = true;
        }
        // As a convenience, we return a reference to the passed stream //
        return stream;
    }
}

Maintenant, pour l'utiliser, vous pouvez simplement faire quelque chose comme ce qui suit:

// Load some data //
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
params.push_back(4);

// Store and print our results to standard out //
std::stringstream param_stream;
std::cout << my::join(param_stream, params.begin(), params.end(), ",").str() << std::endl;

// A quick and dirty way to print directly to standard out //
my::join(std::cout, params.begin(), params.end(), ",") << std::endl;

Notez comment l'utilisation de flux rend cette solution incroyablement flexible car nous pouvons stocker notre résultat dans un flux de chaînes pour le récupérer plus tard, ou nous pouvons écrire directement dans la sortie standard, un fichier, ou même sur une connexion réseau implémentée sous forme de flux. Le type imprimé doit simplement être itérable et compatible avec le flux source. STL fournit divers flux compatibles avec une large gamme de types. Donc tu pourrais vraiment aller en ville avec ça. Du haut de ma tête, votre vecteur peut être int, float, double, string, unsigned int, SomeObject *, etc.

David Peterson
la source
1

J'ai créé un fichier d'en-tête d'assistance pour ajouter une prise en charge de jointure étendue.

Ajoutez simplement le code ci-dessous à votre fichier d'en-tête général et incluez-le si nécessaire.

Exemples d'utilisation:

/* An example for a mapping function. */
ostream&
map_numbers(ostream& os, const void* payload, generic_primitive data)
{
    static string names[] = {"Zero", "One", "Two", "Three", "Four"};
    os << names[data.as_int];
    const string* post = reinterpret_cast<const string*>(payload);
    if (post) {
        os << " " << *post;
    }
    return os;
}

int main() {
    int arr[] = {0,1,2,3,4};
    vector<int> vec(arr, arr + 5);
    cout << vec << endl; /* Outputs: '0 1 2 3 4' */
    cout << join(vec.begin(), vec.end()) << endl; /* Outputs: '0 1 2 3 4' */
    cout << join(vec.begin(), vec.begin() + 2) << endl; /* Outputs: '0 1 2' */
    cout << join(vec.begin(), vec.end(), ", ") << endl; /* Outputs: '0, 1, 2, 3, 4' */
    cout << join(vec.begin(), vec.end(), ", ", map_numbers) << endl; /* Outputs: 'Zero, One, Two, Three, Four' */
    string post = "Mississippi";
    cout << join(vec.begin() + 1, vec.end(), ", ", map_numbers, &post) << endl; /* Outputs: 'One Mississippi, Two mississippi, Three mississippi, Four mississippi' */
    return 0;
}

Le code derrière la scène:

#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <unordered_set>
using namespace std;

#define GENERIC_PRIMITIVE_CLASS_BUILDER(T) generic_primitive(const T& v) { value.as_##T = v; }
#define GENERIC_PRIMITIVE_TYPE_BUILDER(T) T as_##T;

typedef void* ptr;

/** A union that could contain a primitive or void*,
 *    used for generic function pointers.
 * TODO: add more primitive types as needed.
 */
struct generic_primitive {
    GENERIC_PRIMITIVE_CLASS_BUILDER(int);
    GENERIC_PRIMITIVE_CLASS_BUILDER(ptr);
    union {
        GENERIC_PRIMITIVE_TYPE_BUILDER(int);
        GENERIC_PRIMITIVE_TYPE_BUILDER(ptr);
    };
};

typedef ostream& (*mapping_funct_t)(ostream&, const void*, generic_primitive);
template<typename T>
class Join {
public:
    Join(const T& begin, const T& end,
            const string& separator = " ",
            mapping_funct_t mapping = 0,
            const void* payload = 0):
            m_begin(begin),
            m_end(end),
            m_separator(separator),
            m_mapping(mapping),
            m_payload(payload) {}

    ostream&
    apply(ostream& os) const
    {
        T begin = m_begin;
        T end = m_end;
        if (begin != end)
            if (m_mapping) {
                m_mapping(os, m_payload, *begin++);
            } else {
                os << *begin++;
            }
        while (begin != end) {
            os << m_separator;
            if (m_mapping) {
                m_mapping(os, m_payload, *begin++);
            } else {
                os << *begin++;
            }
        }
        return os;
    }
private:
    const T& m_begin;
    const T& m_end;
    const string m_separator;
    const mapping_funct_t m_mapping;
    const void* m_payload;
};

template <typename T>
Join<T>
join(const T& begin, const T& end,
     const string& separator = " ",
     ostream& (*mapping)(ostream&, const void*, generic_primitive) = 0,
     const void* payload = 0)
{
    return Join<T>(begin, end, separator, mapping, payload);
}

template<typename T>
ostream&
operator<<(ostream& os, const vector<T>& vec) {
    return join(vec.begin(), vec.end()).apply(os);
}

template<typename T>
ostream&
operator<<(ostream& os, const list<T>& lst) {
    return join(lst.begin(), lst.end()).apply(os);
}

template<typename T>
ostream&
operator<<(ostream& os, const set<T>& s) {
    return join(s.begin(), s.end()).apply(os);
}

template<typename T>
ostream&
operator<<(ostream& os, const Join<T>& vec) {
    return vec.apply(os);
}
Maor Gaon
la source
1

Voici une solution générique C ++ 11 qui vous permettra de faire

int main() {
    vector<int> v {1,2,3};
    cout << join(v, ", ") << endl;
    string s = join(v, '+').str();
}

Le code est:

template<typename Iterable, typename Sep>
class Joiner {
    const Iterable& i_;
    const Sep& s_;
public:
    Joiner(const Iterable& i, const Sep& s) : i_(i), s_(s) {}
    std::string str() const {std::stringstream ss; ss << *this; return ss.str();}
    template<typename I, typename S> friend std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j);
};

template<typename I, typename S>
std::ostream& operator<< (std::ostream& os, const Joiner<I,S>& j) {
    auto elem = j.i_.begin();
    if (elem != j.i_.end()) {
        os << *elem;
        ++elem;
        while (elem != j.i_.end()) {
            os << j.s_ << *elem;
            ++elem;
        }
    }
    return os;
}

template<typename I, typename S>
inline Joiner<I,S> join(const I& i, const S& s) {return Joiner<I,S>(i, s);}
n.caillou
la source
1

Voici un moyen simple et pratique de convertir des éléments de a vectoren a string:

std::string join(const std::vector<int>& numbers, const std::string& delimiter = ",") {
    std::ostringstream result;
    for (const auto number : numbers) {
        if (result.tellp() > 0) { // not first round
            result << delimiter;
        }
        result << number;
    }
    return result.str();
}

Vous devez #include <sstream>pour ostringstream.

mrts
la source
1

Expansion lors de la tentative de @sbi sur une solution générique qui n'est pas limitée à std::vector<int>ou à un type de chaîne de retour spécifique. Le code présenté ci-dessous peut être utilisé comme ceci:

std::vector<int> vec{ 1, 2, 3 };

// Call modern range-based overload.
auto str     = join( vec,  "," );
auto wideStr = join( vec, L"," );

// Call old-school iterator-based overload.
auto str     = join( vec.begin(), vec.end(),  "," );
auto wideStr = join( vec.begin(), vec.end(), L"," );

Dans le code d'origine, la déduction d'argument de modèle ne fonctionne pas pour produire le bon type de chaîne de retour si le séparateur est une chaîne littérale (comme dans les exemples ci-dessus). Dans ce cas, les typedefs comme Str::value_typedans le corps de la fonction sont incorrects. Le code suppose que Strc'est toujours un type comme std::basic_string, donc il échoue évidemment pour les chaînes littérales.

Pour résoudre ce problème, le code suivant tente de déduire uniquement le caractère type de de l'argument de séparation et l'utilise pour produire un type de chaîne de retour par défaut. Ceci est réalisé en utilisant boost::range_value, qui extrait le type d'élément du type de plage donné .

#include <string>
#include <sstream>
#include <boost/range.hpp>

template< class Sep, class Str = std::basic_string< typename boost::range_value< Sep >::type >, class InputIt >
Str join( InputIt first, const InputIt last, const Sep& sep )
{
    using char_type          = typename Str::value_type;
    using traits_type        = typename Str::traits_type;
    using allocator_type     = typename Str::allocator_type;
    using ostringstream_type = std::basic_ostringstream< char_type, traits_type, allocator_type >;

    ostringstream_type result;

    if( first != last )
    {
        result << *first++;
    }
    while( first != last ) 
    {
        result << sep << *first++;
    }
    return result.str();
}

Maintenant, nous pouvons facilement fournir une surcharge basée sur la plage qui transmet simplement à la surcharge basée sur l'itérateur:

template <class Sep, class Str = std::basic_string< typename boost::range_value<Sep>::type >, class InputRange>
Str join( const InputRange &input, const Sep &sep )
{
    // Include the standard begin() and end() in the overload set for ADL. This makes the 
    // function work for standard types (including arrays), aswell as any custom types 
    // that have begin() and end() member functions or overloads of the standalone functions.
    using std::begin; using std::end;

    // Call iterator-based overload.
    return join( begin(input), end(input), sep );
}

Démo en direct à Coliru

zett42
la source
0

comme l'a fait @capone,

std::string join(const std::vector<std::string> &str_list , 
                 const std::string &delim=" ")
{
    if(str_list.size() == 0) return "" ;
    return std::accumulate( str_list.cbegin() + 1, 
                            str_list.cend(), 
                            str_list.at(0) , 
                            [&delim](const std::string &a , const std::string &b)
                            { 
                                return a + delim + b ;
                            }  ) ; 
}

template <typename ST , typename TT>
std::vector<TT> map(TT (*op)(ST) , const vector<ST> &ori_vec)
{
    vector<TT> rst ;
    std::transform(ori_vec.cbegin() ,
                  ori_vec.cend() , back_inserter(rst) , 
                  [&op](const ST& val){ return op(val)  ;} ) ;
    return rst ;
}

Ensuite, nous pouvons appeler comme suit:

int main(int argc , char *argv[])
{
    vector<int> int_vec = {1,2,3,4} ;
    vector<string> str_vec = map<int,string>(to_string, int_vec) ;
    cout << join(str_vec) << endl ;
    return 0 ;
}

tout comme python:

>>> " ".join( map(str, [1,2,3,4]) )
小 文件
la source
0

J'utilise quelque chose comme ça

namespace std
{

// for strings join
string to_string( string value )
{
    return value;
}

} // namespace std

namespace // anonymous
{

template< typename T >
std::string join( const std::vector<T>& values, char delimiter )
{
    std::string result;
    for( typename std::vector<T>::size_type idx = 0; idx < values.size(); ++idx )
    {
        if( idx != 0 )
            result += delimiter;
        result += std::to_string( values[idx] );
    }
    return result;
}

} // namespace anonymous
Shinshillov
la source
0

J'ai commencé avec la réponse de @ sbi, mais la plupart du temps, j'ai fini par envoyer la chaîne résultante à un flux, ce qui a créé la solution ci-dessous qui peut être transférée vers un flux sans avoir à créer la chaîne complète en mémoire.

Il est utilisé comme suit:

#include "string_join.h"
#include <iostream>
#include <vector>

int main()
{
  std::vector<int> v = { 1, 2, 3, 4 };
  // String version
  std::string str = join(v, std::string(", "));
  std::cout << str << std::endl;
  // Directly piped to stream version
  std::cout << join(v, std::string(", ")) << std::endl;
}

Où string_join.h est:

#pragma once

#include <iterator>
#include <sstream>

template<typename Str, typename It>
class joined_strings
{
  private:
    const It begin, end;
    Str sep;

  public:
    typedef typename Str::value_type char_type;
    typedef typename Str::traits_type traits_type;
    typedef typename Str::allocator_type allocator_type;

  private:
    typedef std::basic_ostringstream<char_type, traits_type, allocator_type>
      ostringstream_type;

  public:
    joined_strings(It begin, const It end, const Str &sep)
      : begin(begin), end(end), sep(sep)
    {
    }

    operator Str() const
    {
      ostringstream_type result;
      result << *this;
      return result.str();
    }

    template<typename ostream_type>
    friend ostream_type& operator<<(
      ostream_type &ostr, const joined_strings<Str, It> &joined)
    {
      It it = joined.begin;
      if(it!=joined.end)
        ostr << *it;
      for(++it; it!=joined.end; ++it)
        ostr << joined.sep << *it;
      return ostr;
    }
};

template<typename Str, typename It>
inline joined_strings<Str, It> join(It begin, const It end, const Str &sep)
{
  return joined_strings<Str, It>(begin, end, sep);
}

template<typename Str, typename Container>
inline joined_strings<Str, typename Container::const_iterator> join(
  Container container, const Str &sep)
{
  return join(container.cbegin(), container.cend(), sep);
}
Nathan Phillips
la source
0

J'ai écrit le code suivant. Il est basé sur C # string.join. Il fonctionne avec std :: string et std :: wstring et de nombreux types de vecteurs. (exemples dans les commentaires)

Appelez ça comme ça:

 std::vector<int> vVectorOfIds = {1, 2, 3, 4, 5};

 std::wstring wstrStringForSQLIn = Join(vVectorOfIds, L',');

Code:

// Generic Join template (mimics string.Join() from C#)
// Written by RandomGuy (stackoverflow) 09-01-2017
// Based on Brian R. Bondy anwser here:
// http://stackoverflow.com/questions/1430757/c-vector-to-string
// Works with char, wchar_t, std::string and std::wstring delimiters
// Also works with a different types of vectors like ints, floats, longs
template<typename T, typename D>
auto Join(const std::vector<T> &vToMerge, const D &delimiter)
{
    // We use std::conditional to get the correct type for the stringstream (char or wchar_t)
    // stringstream = basic_stringstream<char>, wstringstream = basic_stringstream<wchar_t>
    using strType =
        std::conditional<
        std::is_same<D, std::string>::value,
        char,
            std::conditional<
            std::is_same<D, char>::value,
            char,
            wchar_t
            >::type
        >::type;

    std::basic_stringstream<strType> ss;

    for (size_t i = 0; i < vToMerge.size(); ++i)
    {
        if (i != 0)
            ss << delimiter;
        ss << vToMerge[i];
    }
    return ss.str();
}
Type au hasard
la source
0

Voici un moyen simple de convertir un vecteur d'entiers en chaînes.

#include <bits/stdc++.h>
using namespace std;
int main()
{
    vector<int> A = {1, 2, 3, 4};
    string s = "";
    for (int i = 0; i < A.size(); i++)
    {
        s = s + to_string(A[i]) + ",";
    }
    s = s.substr(0, s.length() - 1); //Remove last character
    cout << s;
}
Amit
la source
0

rejoindre en utilisant la fonction de modèle

J'ai utilisé un template functionpour joindre les vectoréléments et j'ai supprimé l' ifinstruction inutile en parcourant uniquement le premier à l'avant-dernier élément duvector , puis en joignant le dernier élément après la forboucle. Cela évite également le besoin de code supplémentaire pour supprimer le séparateur supplémentaire à la fin de la chaîne jointe. Donc, pas de ifdéclarations ralentissant l'itération et pas de séparateur superflu à ranger.

Cela produit un appel de fonction élégante à se joindre à un vectordesstring , integerou double, etc.

J'ai écrit deux versions: l'une renvoie une chaîne; l'autre écrit directement dans un flux.

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;

// Return a string of joined vector items.
template<typename T>
string join(const vector<T>& v, const string& sep)
{
    ostringstream oss;
    const auto LAST = v.end() - 1;
    // Iterate through the first to penultimate items appending the separator.
    for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p)
    {
        oss << *p << sep;
    }
    // Join the last item without a separator.
    oss << *LAST;
    return oss.str();
}

// Write joined vector items directly to a stream.
template<typename T>
void join(const vector<T>& v, const string& sep, ostream& os)
{
    const auto LAST = v.end() - 1;
    // Iterate through the first to penultimate items appending the separator.
    for (typename vector<T>::const_iterator p = v.begin(); p != LAST; ++p)
    {
        os << *p << sep;
    }
    // Join the last item without a separator.
    os << *LAST;
}

int main()
{
    vector<string> strings
    {
        "Joined",
        "from",
        "beginning",
        "to",
        "end"
    };
    vector<int> integers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    vector<double> doubles{ 1.2, 3.4, 5.6, 7.8, 9.0 };

    cout << join(strings, "... ") << endl << endl;
    cout << join(integers, ", ") << endl << endl;
    cout << join(doubles, "; ") << endl << endl;

    join(strings, "... ", cout);
    cout << endl << endl;
    join(integers, ",  ", cout);
    cout << endl << endl;
    join(doubles, ";  ", cout);
    cout << endl << endl;

    return 0;
}

Production

Joined... from... beginning... to... end

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

1.2; 3.4; 5.6; 7.8; 9

Joined... from... beginning... to... end

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

1.2; 3.4; 5.6; 7.8; 9
Tim
la source