Converti un vettore <int> in una stringa


Risposte:


95

Sicuramente non elegante come Python, ma niente è così elegante come Python in C ++.

Potresti usare 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();

Potresti anche usare std::for_eachinvece.


Penso che intendi array.size () non v.size (), no?
Mark Elliot

1
qualunque sia il nome del vettore.
Brian R. Bondy,

2
Dovrebbe essere std::string s = ss.str(). Se vuoi un const char*, usa s.c_str(). (Nota che, sebbene sintatticamente corretto, ss.str().c_str()ti darà un const char*che indica un temporaneo che cesserà di esistere alla fine dell'espressione completa. Ciò fa male.)
sbi

1
perché non usare semplicemente string.append?
Baiyan Huang,

12
la risposta è incompleta senza#include <sstream>
renadeen

43

Usando std :: for_each e lambda puoi fare qualcosa di interessante.

#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=',';
                   });
}

Vedi questa domanda per un piccolo corso che ho scritto. Questo non stamperà la virgola finale. Anche se assumiamo che C ++ 14 continuerà a darci equivalenti basati su intervallo di algoritmi come questo:

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
}

12
Penso che questo non sia del tutto equivalente al join di Python - inserirà un "," in più alla fine.
1800 INFORMAZIONI

2
Non equivalente ma altrettanto elegante (in effetti penso di più ma è solo un'opinione).
Martin York,

20
Ovviamente l'eleganza è soggettiva. Quindi, se tu e altre due persone preferite un codice più lungo e ripetitivo che non funziona, allora è più elegante ;-p
Steve Jessop

1
È possibile ignorare la virgola finale utilizzando la funzione membro string :: substr. Assegna la sottostringa da 0 a n-1 alla variabile del risultato.
Dan,

@SteveJessop: Meglio?
Martin York

24

Puoi usare std :: accumulate. Considera il seguente esempio

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);
                     });

','dovrebbe essere","
Matt

2
@Matt La stringclasse ha un sovraccarico per l' +operatore che può accettare anche caratteri. Quindi ','va bene.
Pavan Manjunath

19

Un'altra alternativa è l'uso di std::copye 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);

Inoltre non è bello come Python. A questo scopo ho creato una joinfunzione:

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;
}

Quindi l'ho usato in questo modo:

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

Potresti chiederti perché sono passato agli iteratori. Bene, in realtà volevo invertire l'array, quindi l'ho usato in questo modo:

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

Idealmente, vorrei creare un modello fino al punto in cui è possibile dedurre il tipo di carattere e utilizzare flussi di stringhe, ma non sono ancora riuscito a capirlo.


Dato che sarebbe troppo per un commento, ho pubblicato una risposta ( stackoverflow.com/questions/1430757/1432040#1432040 ) che tenta di risolvere l'enigma dato nella tua ultima frase.
sbi

La tua joinfunzione può essere utilizzata anche con i vettori? Puoi fare un esempio, sono nuovo in C ++.
Noitidart

Puoi cambiare l'iteratore in un preincremento nella risposta?
Millie Smith

14

Con Boost e C ++ 11 questo potrebbe essere ottenuto in questo modo:

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

Be 'quasi. Ecco l'esempio completo:

#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;
}

Ringraziamo Praetorian .

Puoi gestire qualsiasi tipo di valore come questo:

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);
};

11

Questo è solo un tentativo di risolvere l'enigma dato dall'osservazione di 1800 INFORMATION sulla sua seconda soluzione priva di genericità, non un tentativo di rispondere alla domanda:

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();
}

Funziona sulla mia macchina (TM).


Visual Studio 2013 viene molto confuso dai typedef. Non che avresti potuto
saperlo

@ Jesus: Sto fissando questo per 5 minuti, ma non sono riuscito a capire cosa potrebbe inciampare VS. Può essere più preciso?
sbi

Mi dispiace, penso di aver tentato un join di oggetti senza << sovraccarichi, che ora mi rendo conto che non è appropriato per il tuo codice. Non posso fare in modo che il tuo codice non venga compilato con un vettore di stringhe. In una nota a margine, VS 2013 Community è sia gratuita che ricca di funzionalità, a differenza delle versioni "Express".
Grault

@Jes: dovrebbe funzionare con qualsiasi tipo che può essere trasmesso in streaming (cioè, ha operator<<sovraccaricato). Ovviamente, un tipo senza operator<<potrebbe causare messaggi di errore molto confusi.
sbi

Purtroppo, questo non può essere compilato: join(v.begin(), v.end(), ","). La deduzione dell'argomento del modello non produce il risultato corretto se l' separgomento è una stringa letterale. Il mio tentativo di una soluzione per questo problema . Fornendo anche un sovraccarico basato sulla gamma più moderno.
zett42

7

Molti modelli / idee. Il mio non è così generico o efficiente, ma avevo lo stesso problema e volevo inserirlo nel mix come qualcosa di breve e dolce. Vince sul minor numero di righe ... :)

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);

1
Grazie per il codice semplicistico. Potrebbe essere necessario cambiarlo in "auto &" per evitare le copie extra e ottenere alcuni facili miglioramenti delle prestazioni.
Millie Smith

Invece di usare substr(...), usa pop_back()per rimuovere l'ultimo carattere, diventa molto più chiaro e pulito quindi.
ifyalciner

4

Se vuoi fare std::cout << join(myVector, ",") << std::endl;, puoi fare qualcosa come:

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));
}

Nota, questa soluzione fa l'unione direttamente nel flusso di output piuttosto che creare un buffer secondario e funzionerà con qualsiasi tipo che abbia un operatore << su un ostream.

Funziona anche dove boost::algorithm::join()fallisce, quando hai vector<char*>un file vector<string>.


4
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);

7
Benvenuto in Stack Overflow! Sebbene questo codice possa risolvere il problema, è meglio aggiungere un'elaborazione e spiegare come funziona per le persone che potrebbero non capire questo pezzo di codice.
paper1111

1
L'attuale risposta migliore non è molto più elaborata e questa è la risposta più piccola / più pulita. Non è efficiente come std::stringstreamper gli array di grandi dimensioni perché stringstreamsarà in grado di allocare la memoria in modo ottimistico, portando a prestazioni O (n.log (n)) invece di O (n²) per un array di dimensioni nper questa risposta. Inoltre stringstreampotrebbe non creare stringhe temporanee per to_string(i).
aberaud

2

Mi piace la risposta del 1800. Tuttavia, sposterei la prima iterazione fuori dal ciclo poiché il risultato dell'istruzione if cambia solo una volta dopo la prima iterazione

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;
}

Questo può ovviamente essere ridotto a un minor numero di affermazioni se ti piace:

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;
}

Non dovresti usare post-incremento per tipi di iteratori sconosciuti. Potrebbe essere costoso. (Ovviamente, quando si ha a che fare con le corde, potrebbe non fare molta differenza. Ma una volta che si impara l'abitudine ...)
sbi

L'incremento dei post va bene fintanto che si utilizza il valore temporaneo restituito. es. "risultato.append (* it); ++ it;" è quasi sempre costoso quanto "result.append (* it ++);" il secondo ha una copia extra dell'iteratore.
iain

Oops, ho appena notato l'incremento del post nel ciclo for. copia e incolla errore. Ho corretto il post.
iain

1
@Ian: Quando ho insegnato C ++, ho insistito sui miei studenti da usare ++itranne dove ne avevano davvero bisogno i++perché era l'unico modo in cui non lo dimenticavano quando faceva la differenza. (Era lo stesso con me, BTW.) Avevano già imparato Java, dove tutti i tipi di C-ismi sono in voga e ci sono voluti alcuni mesi (1 lezione + lavoro di laboratorio a settimana), ma alla fine la maggior parte hanno imparato l'abitudine di usare il pre-incremento.
sbi

1
@sbi: acconsento che anch'io prendo sempre il preincremento, il postincremento canaglia derivava dalla copia di qualcun altro per loop e dalla modifica. Nella mia prima risposta ho pensato che fossi preoccupato per "result.append (* it ++)" e non per il ciclo for. Ero un po 'imbarazzato nel vedere l'incremento del post nel ciclo. Alcune persone sembrano seguire il consiglio di non usare l'incremento di post troppo lontano e non usarlo mai o modificarlo anche quando è appropriato. Tuttavia so che ora non rientri in questa categoria.
iain

2

Ci sono alcuni tentativi interessanti per fornire una soluzione elegante al problema. Ho avuto l'idea di utilizzare flussi basati su modelli per rispondere efficacemente al dilemma originale dell'OP. Sebbene questo sia un vecchio post, spero che i futuri utenti che si imbatteranno in questo troveranno la mia soluzione vantaggiosa.

In primo luogo, alcune risposte (inclusa la risposta accettata) non promuovono la riutilizzabilità. Poiché il C ++ non fornisce un modo elegante per unire le stringhe nella libreria standard (che ho visto), diventa importante crearne una flessibile e riutilizzabile. Ecco la mia possibilità:

// 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;
    }
}

Ora per usarlo, potresti semplicemente fare qualcosa di simile a quanto segue:

// 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;

Nota come l'uso degli stream rende questa soluzione incredibilmente flessibile in quanto possiamo memorizzare il nostro risultato in uno stringstream per recuperarlo in seguito, oppure possiamo scrivere direttamente sullo standard out, un file o anche su una connessione di rete implementata come stream. Il tipo in stampa deve essere semplicemente iterabile e compatibile con il flusso di origine. STL fornisce vari flussi compatibili con un'ampia gamma di tipi. Quindi potresti davvero andare in città con questo. Dalla parte superiore della mia testa, il tuo vettore può essere int, float, double, string, unsigned int, SomeObject * e altro.


1

Ho creato un file di intestazione helper per aggiungere un supporto di join esteso.

Basta aggiungere il codice seguente al file di intestazione generale e includerlo quando necessario.

Esempi di utilizzo:

/* 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;
}

Il codice dietro le quinte:

#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);
}

1

Ecco una soluzione C ++ 11 generica che ti permetterà di farlo

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

Il codice è:

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);}

1

Quello che segue è un modo semplice e pratico per convertire gli elementi in a vectorin 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();
}

È necessario #include <sstream>per ostringstream.


1

Espansione sul tentativo di @sbi di una soluzione generica che non sia limitata a std::vector<int>un tipo di stringa di ritorno specifico. Il codice presentato di seguito può essere utilizzato in questo modo:

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"," );

Nel codice originale, la deduzione dell'argomento del modello non funziona per produrre il tipo di stringa di ritorno corretto se il separatore è una stringa letterale (come negli esempi precedenti). In questo caso, i typedef come Str::value_typenel corpo della funzione non sono corretti. Il codice presuppone che Strsia sempre un tipo comestd::basic_string , quindi ovviamente fallisce per i valori letterali di stringa.

Per risolvere questo problema, il codice seguente tenta di dedurre solo il tipo di carattere dall'argomento separatore e lo utilizza per produrre un tipo di stringa di ritorno predefinito. Ciò si ottiene utilizzando boost::range_value, che estrae il tipo di elemento dal tipo di intervallo specificato .

#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();
}

Ora possiamo facilmente fornire un sovraccarico basato su intervallo che inoltra semplicemente al sovraccarico basato su iteratore:

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 );
}

Demo dal vivo a Coliru


0

come ha fatto @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 ;
}

Quindi possiamo chiamare come segue:

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 ;
}

proprio come Python:

>>> " ".join( map(str, [1,2,3,4]) )

0

Uso qualcosa di simile

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

0

Ho iniziato con la risposta di @ sbi, ma la maggior parte delle volte ho finito per convogliare la stringa risultante in un flusso, quindi ho creato la soluzione seguente che può essere inviata a un flusso senza l'overhead di creare la stringa completa in memoria.

È usato come segue:

#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;
}

Dove string_join.h è:

#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);
}

0

Ho scritto il seguente codice. È basato in C # string.join. Funziona con std :: string e std :: wstring e molti tipi di vettori. (esempi nei commenti)

Chiamalo così:

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

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

Codice:

// 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();
}

0

Ecco un modo semplice per convertire un vettore di numeri interi in stringhe.

#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;
}

0

partecipare utilizzando la funzione modello

Ho usato a template functionper unire gli vectorelementi e ho rimosso l' ifistruzione non necessaria iterando solo dal primo al penultimo elemento in vector, quindi unendo l'ultimo elemento dopo il forciclo. Ciò evita anche la necessità di codice aggiuntivo per rimuovere il separatore aggiuntivo alla fine della stringa unita. Quindi noif dichiarazione che rallenti l'iterazione e nessun separatore superfluo da riordinare.

Questo produce un elegante chiamata di funzione di partecipare a una vectordi string, integerodouble , etc.

Ho scritto due versioni: una restituisce una stringa; l'altro scrive direttamente in un flusso.

#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;
}

Produzione

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.