Ubah vektor <int> menjadi string

93

Saya memiliki vector<int>wadah yang memiliki bilangan bulat (misalnya {1,2,3,4}) dan saya ingin mengonversinya menjadi string dalam bentuk

"1,2,3,4"

Apa cara terbersih untuk melakukannya di C ++? Dengan Python, inilah cara saya melakukannya:

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

Jawaban:

96

Jelas tidak seanggun Python, tapi tidak ada yang seanggun Python di C ++.

Anda bisa menggunakan 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();

Anda juga bisa memanfaatkannya std::for_each.

Brian R. Bondy
sumber
Saya pikir yang Anda maksud array.size () bukan v.size (), bukan?
Mark Elliot
1
ya apapun namanya vector nya.
Brian R. Bondy
2
Seharusnya begitu std::string s = ss.str(). Jika Anda ingin const char*, gunakan s.c_str(). (Perhatikan bahwa, meskipun secara sintaksis benar, ss.str().c_str()akan memberi Anda const char*poin itu ke sementara yang akan tidak ada lagi pada akhir ekspresi penuh. Itu menyakitkan.)
sbi
1
mengapa tidak menggunakan string.append saja?
Baiyan Huang
12
jawaban tidak lengkap tanpa#include <sstream>
renadeen
44

Menggunakan std :: for_each dan lambda Anda dapat melakukan sesuatu yang menarik.

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

Lihat pertanyaan ini untuk kelas kecil yang saya tulis. Ini tidak akan mencetak tanda koma. Juga jika kami berasumsi bahwa C ++ 14 akan terus memberi kami persamaan berbasis rentang dari algoritme seperti ini:

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
sumber
12
Saya pikir ini tidak cukup setara dengan gabungan Python - ini akan menyisipkan tambahan "," di akhir.
1800 INFORMASI
2
Tidak setara tapi sama elegannya (sebenarnya menurut saya lebih tapi itu hanya opini).
Martin York
20
Jelas keanggunan itu subjektif. Jadi jika Anda dan dua orang lainnya lebih suka kode yang lebih panjang dan berulang yang tidak berfungsi, maka itu lebih elegan ;-p
Steve Jessop
1
Anda dapat mengabaikan koma terakhir dengan menggunakan fungsi anggota string :: substr. Tetapkan substring dari 0 hingga n-1 ke variabel hasil Anda.
Dan
@SteveJop: Lebih baik?
Martin York
24

Anda dapat menggunakan std :: akumulasi. Perhatikan contoh berikut

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
sumber
','seharusnya","
Matt
2
@Matt stringKelas memiliki kelebihan beban untuk +operator yang dapat menerima karakter juga. Jadi ','baik-baik saja.
Pavan Manjunath
19

Alternatif lain adalah penggunaan std::copydan ostream_iteratorkelas:

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

Juga tidak sebaik Python. Untuk tujuan ini, saya membuat sebuah joinfungsi:

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

Kemudian gunakan seperti ini:

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

Anda mungkin bertanya mengapa saya lulus di iterator. Sebenarnya saya ingin membalikkan array, jadi saya menggunakannya seperti ini:

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

Idealnya, saya ingin membuat template ke titik di mana ia dapat menyimpulkan jenis karakter, dan menggunakan aliran string, tetapi saya belum bisa mengetahuinya.

1800 INFORMASI
sumber
Karena akan terlalu banyak untuk komentar, saya telah memposting jawaban ( stackoverflow.com/questions/1430757/1432040#1432040 ) yang mencoba memecahkan teka-teki yang diberikan dalam kalimat terakhir Anda.
sbi
joinFungsi Anda bisa digunakan dengan vektor juga? Tolong beri contoh, saya baru mengenal C ++.
Noitidart
Bisakah Anda mengubah iterator menjadi preincrement dalam jawaban?
Millie Smith
14

Dengan Boost dan C ++ 11 ini dapat dicapai seperti ini:

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

Hampir saja. Berikut contoh lengkapnya:

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

Penghargaan untuk Praetorian .

Anda dapat menangani tipe nilai apa pun seperti ini:

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

Ini hanyalah upaya untuk memecahkan teka-teki yang diberikan oleh komentar 1800 INFORMATION tentang solusi keduanya yang kurang murah hati, bukan upaya untuk menjawab pertanyaan:

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

Bekerja di Mesin Saya (TM).

sbi
sumber
Visual Studio 2013 menjadi sangat bingung dengan typedefs. Bukan berarti Anda bisa mengetahuinya di tahun 2009.
Grault
@Jes: Saya sekarang telah melihat ini selama 5 menit, tetapi tidak tahu VS mungkin tersandung. Bisakah Anda lebih spesifik?
sbi
Maaf, saya pikir saya telah mencoba menggabungkan objek tanpa << kelebihan beban, yang sekarang saya sadari tidak sesuai untuk kode Anda. Saya tidak dapat menyebabkan kode Anda tidak dapat dikompilasi dengan vektor string. Di samping catatan, VS 2013 Community gratis dan berfitur lengkap, tidak seperti versi "Express".
Grault
@Jes: Ini harus bekerja dengan semua jenis yang dapat di-streaming (mis., operator<<Kelebihan beban). Tentu saja, tipe tanpa operator<<dapat menyebabkan pesan kesalahan yang sangat membingungkan.
sbi
Sayangnya, ini tidak kompilasi: join(v.begin(), v.end(), ","). Pemotongan argumen template tidak memberikan hasil yang benar jika separgumennya adalah string literal. Upaya saya mencari solusi untuk masalah ini . Juga menyediakan overload berbasis jangkauan yang lebih modern.
zett42
7

Banyak template / ide. Milik saya tidak umum atau efisien, tetapi saya hanya memiliki masalah yang sama dan ingin memasukkan ini ke dalam campuran sebagai sesuatu yang pendek dan manis. Ia menang pada jumlah baris terpendek ... :)

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
sumber
1
Terima kasih atas kodenya yang sederhana. Mungkin ingin mengubahnya menjadi "otomatis &" untuk menghindari salinan tambahan dan mendapatkan beberapa peningkatan kinerja yang mudah.
Millie Smith
Alih-alih menggunakan substr(...), gunakan pop_back()untuk menghapus karakter terakhir, menjadi lebih jelas dan bersih.
ifyalciner
4

Jika Anda ingin melakukannya std::cout << join(myVector, ",") << std::endl;, Anda dapat melakukan sesuatu seperti:

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

Catatan, solusi ini melakukan join langsung ke output stream daripada membuat buffer sekunder dan akan bekerja dengan semua tipe yang memiliki operator << ke sebuah ostream.

Ini juga berfungsi jika boost::algorithm::join()gagal, jika Anda memiliki a, vector<char*>bukan a vector<string>.

mheyman
sumber
4
string s;
for (auto i : v)
    s += (s.empty() ? "" : ",") + to_string(i);
chenfy27
sumber
8
Selamat datang di Stack Overflow! Meskipun kode ini dapat menyelesaikan masalah, yang terbaik adalah menambahkan elaborasi dan menjelaskan cara kerjanya untuk orang yang mungkin tidak memahami bagian kode ini.
kertas 1111
1
Jawaban teratas saat ini tidak lebih rumit, dan ini adalah jawaban kerja terkecil / terbersih. Tidak seefisien std::stringstreamuntuk array besar karena stringstreamakan dapat mengalokasikan memori secara optimal, yang mengarah ke performa O (n.log (n)) alih-alih O (n²) untuk ukuran array nuntuk jawaban ini. Juga stringstreammungkin tidak membuat string sementara untuk to_string(i).
aberaud
2

Saya suka jawaban tahun 1800-an. Namun saya akan memindahkan iterasi pertama keluar dari loop sebagai hasil dari pernyataan if hanya berubah satu kali setelah iterasi pertama

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

Ini tentu saja dapat dikurangi menjadi lebih sedikit pernyataan jika Anda suka:

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
sumber
Anda tidak boleh menggunakan kenaikan pasca untuk jenis iterator yang tidak diketahui. Itu mungkin mahal. (Tentu saja, ketika berurusan dengan string, itu mungkin tidak membuat banyak perbedaan. Tapi begitu Anda mempelajari kebiasaannya ...)
sbi
Kenaikan posting baik-baik saja selama Anda menggunakan nilai tempory yang dikembalikan. misalnya "result.append (* it); ++ it;" hampir selalu semahal "result.append (* it ++);" yang kedua memiliki satu salinan tambahan dari iterator.
iain
Ups, saya baru saja melihat kenaikan pos di loop for. salin dan tempel kesalahan. Saya telah memperbaiki pos tersebut.
iain
1
@Ian: Ketika saya mengajar C ++, saya mendorong siswa saya untuk menggunakannya ++ikecuali di mana mereka benar-benar membutuhkannya i++karena itulah satu-satunya cara mereka tidak akan melupakan ini ketika itu membuat perbedaan. (Itu sama dengan saya, BTW.) Mereka telah belajar Java sebelumnya, di mana semua jenis C-isme sedang populer dan butuh beberapa bulan (1 kuliah + praktikum per minggu), tetapi pada akhirnya sebagian besar mereka mempelajari kebiasaan menggunakan kenaikan awal.
sbi
1
@sbi: setuju Saya selalu default ke preincrement juga, postincrement nakal datang dari menyalin orang lain untuk loop dan mengubahnya. Dalam balasan pertama saya, saya pikir Anda khawatir tentang "result.append (* it ++)" dan bukan perulangan for. Saya sedikit malu melihat kenaikan posting di loop. Beberapa orang tampaknya mengikuti saran untuk tidak menggunakan kenaikan pos terlalu jauh dan tidak pernah menggunakan atau mengubahnya bahkan ketika itu sesuai. Namun saya tahu sekarang Anda tidak termasuk dalam kategori ini.
iain
2

Ada beberapa upaya menarik untuk memberikan solusi elegan untuk masalah tersebut. Saya punya ide untuk menggunakan aliran template untuk secara efektif menjawab dilema awal OP. Meskipun ini adalah posting lama, saya berharap pengguna di masa depan yang menemukan ini akan menemukan solusi saya bermanfaat.

Pertama, beberapa jawaban (termasuk jawaban yang diterima) tidak mendukung kegunaan kembali. Karena C ++ tidak menyediakan cara yang elegan untuk menggabungkan string di pustaka standar (yang telah saya lihat), menjadi penting untuk membuat yang fleksibel dan dapat digunakan kembali. Inilah kesempatan saya:

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

Sekarang untuk menggunakan ini, Anda cukup melakukan sesuatu seperti berikut:

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

Perhatikan bagaimana penggunaan aliran membuat solusi ini sangat fleksibel karena kami dapat menyimpan hasil kami dalam aliran string untuk mengklaimnya kembali nanti, atau kami dapat menulis langsung ke keluaran standar, file, atau bahkan ke koneksi jaringan yang diimplementasikan sebagai aliran. Jenis yang dicetak harus dapat diulang dan kompatibel dengan aliran sumber. STL menyediakan berbagai aliran yang kompatibel dengan berbagai jenis. Jadi Anda benar-benar bisa pergi ke kota dengan ini. Di luar kepala saya, vektor Anda bisa berupa int, float, double, string, unsigned int, SomeObject *, dan banyak lagi.

David Peterson
sumber
1

Saya telah membuat file header helper untuk menambahkan dukungan perpanjangan bergabung.

Cukup tambahkan kode di bawah ini ke file header umum Anda dan sertakan bila diperlukan.

Contoh Penggunaan:

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

Kode di balik layar:

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

Berikut solusi C ++ 11 generik yang memungkinkan Anda melakukannya

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

Kodenya adalah:

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

Berikut ini adalah cara sederhana dan praktis untuk mengubah elemen menjadi vectora 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();
}

Anda perlu #include <sstream>untuk ostringstream.

mrts
sumber
1

Memperluas upaya @sbi pada solusi umum yang tidak dibatasi std::vector<int>atau tipe string kembalian tertentu. Kode yang disajikan di bawah ini dapat digunakan seperti ini:

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

Dalam kode asli, pengurangan argumen template tidak berfungsi untuk menghasilkan tipe string pengembalian yang benar jika pemisahnya adalah string literal (seperti dalam contoh di atas). Dalam hal ini, typedefs seperti Str::value_typedi badan fungsi salah. Kode mengasumsikan bahwa Strselalu tipe seperti std::basic_string, jadi jelas gagal untuk string literal.

Untuk memperbaikinya, kode berikut mencoba untuk menyimpulkan hanya tipe karakter dari argumen pemisah dan menggunakannya untuk menghasilkan tipe string kembali default. Ini dicapai dengan menggunakan boost::range_value, yang mengekstrak tipe elemen dari tipe rentang yang diberikan .

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

Sekarang kita dapat dengan mudah menyediakan overload berbasis rentang yang hanya meneruskan ke overload berbasis iterator:

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 Langsung di Coliru

zett42
sumber
0

seperti yang dilakukan @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 ;
}

Kemudian kita bisa memanggil seperti berikut:

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

seperti python:

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

Saya menggunakan sesuatu seperti ini

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.dll
sumber
0

Saya mulai dengan jawaban @ sbi tetapi sebagian besar waktu berakhir dengan menyalurkan string yang dihasilkan ke aliran sehingga menciptakan solusi di bawah ini yang dapat disalurkan ke aliran tanpa overhead untuk membuat string penuh dalam memori.

Ini digunakan sebagai berikut:

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

Di mana string_join.h adalah:

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

Saya telah menulis kode berikut. Itu berbasis di C # string.join. Ia bekerja dengan std :: string dan std :: wstring dan banyak jenis vektor. (contoh di komentar)

Sebut saja seperti ini:

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

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

Kode:

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

Berikut adalah cara mudah untuk mengubah vektor bilangan bulat menjadi string.

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

bergabunglah menggunakan fungsi template

Saya menggunakan a template functionuntuk menggabungkan vectoritem, dan menghapus ifpernyataan yang tidak perlu dengan mengulang hanya item pertama ke item kedua dari belakang di vector, lalu bergabung dengan item terakhir setelah forloop. Ini juga meniadakan kebutuhan kode tambahan untuk menghapus pemisah ekstra di akhir string yang digabungkan. Jadi, tidak ada ifpernyataan yang memperlambat iterasi dan tidak ada pemisah berlebihan yang perlu dirapikan.

Ini menghasilkan panggilan fungsi elegan untuk bergabung dengan vectordari string, integeratau double, dll

Saya menulis dua versi: satu mengembalikan string; yang lainnya menulis langsung ke aliran.

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

Keluaran

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
sumber