Converta um vetor <int> em uma string

92

Eu tenho um vector<int>contêiner que tem números inteiros (por exemplo, {1,2,3,4}) e gostaria de converter para uma string da forma

"1,2,3,4"

Qual é a maneira mais limpa de fazer isso em C ++? Em Python, eu faria isso:

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

Respostas:

95

Definitivamente, não é tão elegante quanto Python, mas nada é tão elegante quanto Python em C ++.

Você poderia usar um 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();

Você também pode usar em std::for_eachvez disso.

Brian R. Bondy
fonte
Eu acho que você quer dizer array.size () não v.size (), não?
Mark Elliot,
1
ya qualquer que seja o vetor é chamado.
Brian R. Bondy,
2
Isso deveria ser std::string s = ss.str(). Se você quiser um const char*, use s.c_str(). (Observe que, embora sintaticamente correto, ss.str().c_str()dará a você um const char*que aponta para um temporário que deixará de existir no final da expressão completa. Isso dói.)
sbi
1
por que não usar string.append?
Baiyan Huang
12
a resposta está incompleta sem#include <sstream>
renadeen
43

Usando std :: for_each e lambda você pode fazer algo 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=',';
                   });
}

Veja esta pergunta para uma pequena aula que escrevi. Isso não imprimirá a vírgula final. Além disso, se assumirmos que o C ++ 14 continuará a nos fornecer equivalentes baseados em intervalo de algoritmos como este:

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
fonte
12
Acho que isso não é exatamente equivalente à junção do Python - ele irá inserir um "," extra no final.
1800 INFORMAÇÕES
2
Não equivalente, mas igualmente elegante (na verdade, acho mais, mas isso é apenas uma opinião).
Martin York,
20
Obviamente, a elegância é subjetiva. Então, se você e duas outras pessoas preferem um código mais longo e repetitivo que não funciona, é mais elegante ;-p
Steve Jessop
1
Você pode ignorar a vírgula final usando a função de membro string :: substr. Atribua a substring de 0 a n-1 à sua variável de resultado.
Dan,
@SteveJessop: Melhor?
Martin York
24

Você pode usar std :: acumular. Considere o seguinte exemplo

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
fonte
','deveria ser","
Matt
2
@Matt A stringclasse tem uma sobrecarga para o +operador que também pode aceitar caracteres. Então ','está tudo bem.
Pavan Manjunath
19

Outra alternativa é o uso de std::copye da 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);

Também não é tão bom quanto Python. Para isso, criei uma joinfunção:

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

Então usei assim:

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

Você pode perguntar por que passei nos iteradores. Bem, na verdade, eu queria inverter a matriz, então usei assim:

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

Idealmente, eu gostaria de modelar até o ponto em que pudesse inferir o tipo char e usar string-streams, mas ainda não consegui descobrir isso.

1800 INFORMAÇÕES
fonte
Como seria muito para um comentário, postei uma resposta ( stackoverflow.com/questions/1430757/1432040#1432040 ) que tenta resolver o enigma fornecido em sua última frase.
sbi de
Sua joinfunção pode ser usada com vetores também? Você pode dar um exemplo, eu sou novo em C ++.
Noitidart de
Você pode alterar o iterador para um pré-incremento na resposta?
Millie Smith
14

Com Boost e C ++ 11, isso pode ser feito assim:

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

Bem, quase. Aqui está o exemplo 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;
}

Crédito para Pretoriano .

Você pode lidar com qualquer tipo de valor como este:

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
fonte
11

Esta é apenas uma tentativa de resolver o enigma dado pela observação de 1800 INFORMAÇÕES sobre sua segunda solução carente de genericidade, não uma tentativa de responder à pergunta:

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

Funciona na minha máquina (TM).

sbi
fonte
O Visual Studio 2013 fica muito confuso com os typedefs. Não que você pudesse saber disso em 2009.
Grault,
@Jes: Estou olhando para isso há 5 minutos, mas não consegui descobrir no que o VS poderia tropeçar. Você pode ser mais específico?
sbi de
Sinto muito, acho que tentei uma junção de objetos sem sobrecargas <<, o que agora percebo é impróprio para o seu código. Não posso fazer com que seu código não compile com um vetor de strings. Por outro lado, o VS 2013 Community é gratuito e repleto de recursos, ao contrário das versões "Express".
Grault
@Jes: Isso deve funcionar com qualquer tipo que possa ser transmitido (ou seja, operator<<sobrecarregado). Obviamente, um tipo sem operator<<pode causar mensagens de erro muito confusas.
sbi
Infelizmente, isso não compila: join(v.begin(), v.end(), ","). A dedução do argumento do modelo não produz o resultado correto se o separgumento for um literal de string. Minha tentativa de uma solução para este problema . Também oferece uma sobrecarga baseada em alcance mais moderna.
zett42
7

Muitos modelos / ideias. O meu não é tão genérico ou eficiente, mas eu simplesmente tive o mesmo problema e queria jogar isso na mistura como algo curto e doce. Ele vence no menor número de linhas ... :)

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
fonte
1
Obrigado pelo código simplista. Talvez você queira alterá-lo para "auto e" para evitar as cópias extras e obter alguns ganhos de desempenho fáceis.
Millie Smith
Em vez de usar substr(...), use pop_back()para remover o último caractere, torna-se muito mais claro e limpo então.
ifyalciner
4

Se quiser fazer std::cout << join(myVector, ",") << std::endl;, você pode fazer algo como:

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

Observe que esta solução faz a junção diretamente no fluxo de saída em vez de criar um buffer secundário e funcionará com qualquer tipo que tenha um operador << em um ostream.

Isso também funciona onde boost::algorithm::join()falha, quando você tem um em vector<char*>vez de um vector<string>.

mheyman
fonte
4
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);
chenfy27
fonte
7
Bem-vindo ao Stack Overflow! Embora esse código possa resolver o problema, é melhor adicionar mais detalhes e explicar como ele funciona para pessoas que podem não entender esse trecho de código.
paper1111
1
A resposta principal atual não é muito mais elaborada e esta é a resposta de trabalho menor / mais limpa. Não é tão eficiente quanto std::stringstreampara grandes arrays porque stringstreamserá capaz de alocar memória de forma otimista, levando a desempenho O (n.log (n)) em vez de O (n²) para um array de tamanho npara esta resposta. Também stringstreamnão pode construir strings temporárias para to_string(i).
aberaud
2

Gosto da resposta de 1800. No entanto, eu moveria a primeira iteração para fora do loop, pois o resultado da instrução if só muda uma vez após a primeira iteração

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

É claro que isso pode ser reduzido a menos declarações, se você quiser:

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
fonte
Você não deve usar pós-incremento para tipos de iteradores desconhecidos. Isso pode ser caro. (Claro, ao lidar com cordas, isso pode não fazer muita diferença. Mas depois que você aprender o hábito ...)
sbi
O incremento posterior é adequado, desde que você use o valor temporário que é retornado. por exemplo, "result.append (* it); ++ it;" quase sempre é tão caro quanto "result.append (* it ++);" o segundo tem uma cópia extra do iterador.
iain
Opa, acabei de detectar o incremento de postagem no loop for. copiar e colar erro. Eu consertei a postagem.
iain
1
@Ian: Quando ensinei C ++, martelei meus alunos para usarem, ++iexceto onde realmente precisassem, i++porque essa era a única maneira de eles não se esquecerem disso quando fizesse a diferença. (Foi o mesmo comigo, BTW.) Eles tinham aprendido Java antes, onde todos os tipos de C-ismos estão em voga e levaram alguns meses (1 aula + trabalho de laboratório por semana), mas no final a maior parte eles aprenderam o hábito de usar o pré-incremento.
sbi
1
@sbi: concordo que sempre padrão para pré-incremento também, o falso pós-incremento veio de copiar o loop de outra pessoa e alterá-lo. Em minha primeira resposta, pensei que você estava preocupado com "result.append (* it ++)" e não com o loop for. Fiquei um pouco envergonhado ao ver o incremento do post no loop. Algumas pessoas parecem seguir o conselho de não usar o pós-incremento muito longe e nunca usá-lo ou alterá-lo, mesmo quando apropriado. No entanto, agora sei que você não se enquadra nesta categoria.
iain
2

Existem algumas tentativas interessantes de fornecer uma solução elegante para o problema. Tive a ideia de usar fluxos de modelo para responder com eficácia ao dilema original do OP. Embora esta seja uma postagem antiga, espero que os futuros usuários que encontrarem isso achem minha solução benéfica.

Primeiro, algumas respostas (incluindo a resposta aceita) não promovem a reutilização. Uma vez que C ++ não fornece uma maneira elegante de juntar strings na biblioteca padrão (que eu vi), torna-se importante criar uma que seja flexível e reutilizável. Aqui está minha 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;
    }
}

Agora, para usar isso, você pode simplesmente fazer algo como o seguinte:

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

Observe como o uso de streams torna esta solução incrivelmente flexível, pois podemos armazenar nosso resultado em um stringstream para recuperá-lo mais tarde, ou podemos gravar diretamente na saída padrão, um arquivo ou mesmo em uma conexão de rede implementada como um stream. O tipo que está sendo impresso deve ser simplesmente iterável e compatível com o fluxo de origem. O STL fornece vários fluxos compatíveis com uma grande variedade de tipos. Então você poderia realmente ir para a cidade com isso. No topo da minha cabeça, seu vetor pode ser int, float, double, string, unsigned int, SomeObject * e muito mais.

David Peterson
fonte
1

Criei um arquivo de cabeçalho auxiliar para adicionar um suporte estendido de junção.

Basta adicionar o código abaixo ao seu arquivo de cabeçalho geral e incluí-lo quando necessário.

Exemplos de uso:

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

O código por trás da cena:

#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
fonte
1

Aqui está uma solução genérica C ++ 11 que permitirá que você faça

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

O código é:

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
fonte
1

O seguinte é uma maneira simples e prática para converter elementos em um vectora um 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();
}

Você precisa #include <sstream>para ostringstream.

mrts
fonte
1

Expandindo a tentativa de @sbi em uma solução genérica que não está restrita a std::vector<int>um tipo específico de string de retorno. O código apresentado a seguir pode ser usado assim:

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

No código original, a dedução do argumento do modelo não funciona para produzir o tipo de string de retorno correto se o separador for um literal de string (como nos exemplos acima). Neste caso, os typedefs como Str::value_typeno corpo da função estão incorretos. O código assume que Stré sempre um tipo semelhante std::basic_string, portanto, obviamente falha para literais de string.

Para corrigir isso, o código a seguir tenta deduzir apenas o tipo de caractere do argumento separador e usa isso para produzir um tipo de string de retorno padrão. Isso é obtido usando boost::range_value, que extrai o tipo de elemento do tipo de intervalo fornecido .

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

Agora podemos facilmente fornecer uma sobrecarga baseada em intervalo que simplesmente encaminha para a sobrecarga baseada em iterador:

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

Demonstração ao vivo no Coliru

zett42
fonte
0

como @capone fez,

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

Então podemos chamar o seguinte:

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

assim como python:

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

Eu uso algo assim

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
fonte
0

Comecei com a resposta de @sbi, mas na maioria das vezes acabei canalizando a string resultante para um stream, então criei a solução abaixo que pode ser canalizada para um stream sem a sobrecarga de criar a string completa na memória.

É usado da seguinte forma:

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

Onde 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);
}
Nathan Phillips
fonte
0

Eu escrevi o seguinte código. É baseado em C # string.join. Funciona com std :: string e std :: wstring e muitos tipos de vetores. (exemplos em comentários)

Chame assim:

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

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

Código:

// 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();
}
Cara aleatório
fonte
0

Esta é uma maneira fácil de converter um vetor de inteiros em strings.

#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
fonte
0

junte-se usando a função de modelo

Usei a template functionpara juntar os vectoritens e removi a ifinstrução desnecessária iterando apenas do primeiro ao penúltimo item no e vector, em seguida, juntando o último item após o forloop. Isso também elimina a necessidade de código extra para remover o separador extra no final da string unida. Portanto, nenhuma ifinstrução retardando a iteração e nenhum separador supérfluo que precise ser arrumado.

Isso produz uma chamada de função elegante para participar de um vectordos string, integerou double, etc.

Escrevi duas versões: uma retorna uma string; o outro grava diretamente em um fluxo.

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

Resultado

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
fonte