<int> vektörünü dizeye dönüştürme


Yanıtlar:


96

Kesinlikle Python kadar zarif değil, ancak hiçbir şey C ++ 'da Python kadar zarif değildir.

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

Bunun std::for_eachyerine kullanabilirsiniz .


Sanırım dizi.size () v.size () değil mi demek istediniz?
Mark Elliot

1
ya vektöre ne denirse.
Brian R. Bondy

2
Bu olmalı std::string s = ss.str(). Bir istiyorsanız const char*, kullanın s.c_str(). (Sözdizimsel olarak doğru olsa da, ss.str().c_str()size const char*tam ifadenin sonunda var olmayacak geçici bir işaret vereceğini unutmayın . Bu acıtıyor.)
sbi

1
neden string.append'i kullanmıyorsunuz?
Baiyan Huang

12
cevap olmadan eksik#include <sstream>
renadeen

44

Std :: for_each ve lambda'yı kullanarak ilginç bir şey yapabilirsiniz.

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

Yazdığım küçük bir ders için bu soruyu görün . Bu, sondaki virgülü yazdırmaz. Ayrıca, C ++ 14'ün bize aşağıdaki gibi aralık tabanlı algoritma eşdeğerlerini vermeye devam edeceğini varsayarsak:

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
Sanırım bu Python'un birleşimine tam olarak denk değil - sonuna fazladan bir "" ekleyecektir.
1800 BİLGİ

2
Eşdeğer değil ama bir o kadar zarif (aslında daha çok öyle düşünüyorum ama bu sadece bir fikir).
Martin York

20
Açıkçası zarafet özneldir. Yani, siz ve diğer iki kişi çalışmayan daha uzun, daha tekrarlayan kodu tercih ederseniz, o zaman daha zariftir ;-p
Steve Jessop

1
String :: substr üye işlevini kullanarak son virgülü yok sayabilirsiniz. 0'dan n-1'e alt dizeyi sonuç değişkeninize atayın.
Dan

@SteveJessop: Daha iyi mi?
Martin York

24

Std :: biriktirmek kullanabilirsiniz. Aşağıdaki örneği düşünün

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

','olmalı","
Matt

2
@Matt stringSınıf, +karakterleri de kabul edebilen operatör için bir aşırı yüklemeye sahiptir . Yani ','iyi.
Pavan Manjunath

19

Diğer bir alternatif, kullanımı std::copyve ostream_iteratorsınıfıdır:

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

Ayrıca Python kadar güzel değil. Bu amaçla bir joinfonksiyon oluşturdum :

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

Sonra şöyle kullandım:

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

Yinelemelerde neden geçtiğimi sorabilirsiniz. Aslında diziyi tersine çevirmek istedim, bu yüzden şu şekilde kullandım:

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

İdeal olarak, char türünü çıkarabileceği ve dizgi akışlarını kullanabileceği noktaya şablon yapmak isterdim, ancak bunu henüz çözemedim.


Bir yorum için çok fazla olacağı için , son cümlenizde verilen bilmeceyi çözmeye çalışan bir cevap ( stackoverflow.com/questions/1430757/1432040#1432040 ) gönderdim .
sbi

Sizin joinişlevi de vektörler ile kullanılabilir? Lütfen örnek verebilir misiniz, C ++ 'da yeniyim.
Noitidart

Yineleyiciyi yanıtta bir ön artış olarak değiştirebilir misiniz?
Millie Smith

14

Boost ve C ++ 11 ile bu şu şekilde elde edilebilir:

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

Neredeyse. İşte tam örnek:

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

Praetorian'a kredi .

Bunun gibi herhangi bir değer türünü ele alabilirsiniz:

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

Bu, 1800 INFORMATION'ın ikinci çözümüne ilişkin genellikten yoksun açıklamasının verdiği bilmeceyi çözme girişimi, soruyu cevaplama girişimi değil:

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

My Machine (TM) Üzerinde Çalışır.


Visual Studio 2013, yazıtipleri tarafından çok karıştırılıyor. Bunu 2009'da bildiğinizden değil.
Grault

@Jes: 5 dakikadır buna bakıyordum, ancak VS'nin neye takılıp düşebileceğini anlayamadım. Daha spesifik olabilir misin?
sbi

Üzgünüm, << aşırı yükler olmadan nesneleri birleştirmeye çalıştığımı düşünüyorum, bunun kodunuz için uygun olmadığını şimdi anladım. Kodunuzun bir dizi vektörüyle derlenmemesine neden olamam. Bir yan not olarak, VS 2013 Community, "Express" sürümlerin aksine hem ücretsiz hem de özelliklidir.
Grault

@Jes: Bu, akış yapılabilen (yani operator<<aşırı yüklenmiş) herhangi bir türle çalışmalıdır . Elbette, olmayan bir tür operator<<çok kafa karıştırıcı hata mesajlarına neden olabilir.
sbi

Ne yazık ki, bu derleme değil: join(v.begin(), v.end(), ","). Bağımsız sepdeğişken bir dizge dizgesiyse, şablon bağımsız değişken çıkarımı doğru sonucu üretmez . Bu sorun için bir çözüm girişimim . Ayrıca daha modern bir menzile dayalı aşırı yük sağlar.
zett42

7

Çok sayıda şablon / fikir. Benimki o kadar genel ya da verimli değil, ama aynı problemi yaşadım ve bunu kısa ve tatlı bir şey olarak karışıma dahil etmek istedim. En kısa sayıda hatta kazanır ... :)

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
Basit kod için teşekkürler. Fazladan kopyalardan kaçınmak ve bazı kolay performans kazançları elde etmek için bunu "otomatik &" olarak değiştirmek isteyebilirsiniz.
Millie Smith

Kullanmak yerine son karakteri kaldırmak için substr(...)kullanın pop_back(), o zaman çok daha net ve temiz hale gelir.
ifyalciner

4

Yapmak istiyorsan std::cout << join(myVector, ",") << std::endl;, şöyle bir şey yapabilirsin:

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

Bu çözümün, ikincil bir tampon oluşturmak yerine doğrudan çıkış akışına birleştirmeyi gerçekleştirdiğini ve bir ostream üzerinde << operatörü olan tüm türlerle çalışacağını unutmayın.

Bu aynı zamanda boost::algorithm::join()başarısız olduğunda, a vector<char*>yerine bir vector<string>.


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

8
Stack Overflow'a hoş geldiniz! Bu kod sorunu çözebilirken, en iyisi ayrıntı eklemek ve bu kod parçasını anlamayan kişiler için nasıl çalıştığını açıklamaktır.
paper1111

1
Şu anki en iyi cevap daha ayrıntılı değil ve bu en küçük / en temiz çalışan cevap. std::stringstreamBüyük diziler için olduğu kadar verimli değil, çünkü stringstreambelleği iyimser bir şekilde tahsis edebilecek ve nbu yanıt için bir boyut dizisi için O (n²) yerine O (n.log (n)) performansına yol açacak . Ayrıca stringstreamiçin geçici dizeler oluşturamayabilir to_string(i).
aberaud

2

1800'ün cevabını beğendim. Bununla birlikte, if ifadesinin ilk yinelemeden sonra yalnızca bir kez değişmesinin sonucu olarak ilk yinelemeyi döngüden çıkarırdım.

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

Bu, tabii ki, isterseniz daha az ifadeye indirgenebilir:

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

Bilinmeyen yineleme türleri için artım sonrası kullanmamalısınız. Bu pahalı olabilir. (Elbette, dizelerle uğraşırken, bu pek bir fark yaratmayabilir. Ama alışkanlığı öğrendikten sonra ...)
sbi

Döndürülen geçici değeri kullandığınız sürece sonradan artış iyidir. ör. "result.append (* it); ++ it;" neredeyse her zaman "result.append (* it ++);" kadar pahalıdır ikincisi yineleyicinin fazladan bir kopyasına sahiptir.
iain

Oops, for döngüsünde gönderi artışını belirledim. kopyalama ve yapıştırma hatası. Yazıyı düzelttim.
iain

1
@Ian: C ++ 'ı öğrettiğimde, öğrencilerime ++igerçekten ihtiyaç duydukları yerler dışında kullanmak için zorlandım i++çünkü bir fark yarattığında bunu unutmamalarının tek yolu buydu. (Benim için de aynıydı, BTW.) Java'yı daha önce öğrenmişlerdi, burada her türlü C-izm revaçta ve birkaç ay sürdü (haftada 1 ders + laboratuvar çalışması), ama sonunda çoğu ön artış kullanma alışkanlığını öğrendiler.
sbi

1
@sbi: kabul etti Her zaman ön artırmayı da varsayılan olarak ayarladım, sahte postincrement başka birinin döngü için kopyalanması ve değiştirilmesinden geldi. İlk cevabımda for döngüsü değil "result.append (* it ++)" konusunda endişelendiğini düşündüm. Döngüdeki gönderi artışını görmekten biraz utandım. Bazı insanlar, gönderi artışını çok fazla kullanmama tavsiyesine uyuyor ve uygun olsa bile asla kullanmıyor veya değiştirmiyor. Ancak şimdi bu kategoriye girmediğini biliyorum.
iain

2

Soruna zarif bir çözüm sağlamaya yönelik bazı ilginç girişimler var. OP'nin orijinal ikilemine etkili bir şekilde cevap vermek için şablonlu akışları kullanma fikrim vardı. Bu eski bir gönderi olsa da, bununla karşılaşan gelecekteki kullanıcıların çözümümü faydalı bulacağını umuyorum.

İlk olarak, bazı yanıtlar (kabul edilen yanıt dahil) yeniden kullanılabilirliği desteklemez. C ++ dizeleri standart kitaplıkta (gördüğüm) birleştirmek için zarif bir yol sağlamadığından, esnek ve yeniden kullanılabilir bir tane oluşturmak önemli hale geliyor. İşte ona şansım:

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

Şimdi bunu kullanmak için aşağıdakine benzer bir şey yapabilirsiniz:

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

Akarsu kullanımının bu çözümü nasıl inanılmaz derecede esnek hale getirdiğine dikkat edin, çünkü sonucumuzu daha sonra geri almak için bir dizgi akışında saklayabiliriz veya doğrudan standart çıkışa, bir dosyaya veya hatta akış olarak uygulanan bir ağ bağlantısına yazabiliriz. Yazdırılan tür basitçe yinelenebilir ve kaynak akışıyla uyumlu olmalıdır. STL, çok çeşitli türlerle uyumlu çeşitli akışlar sağlar. Böylece gerçekten bununla şehre gidebilirsin. Kafamın dışında, vektörünüz int, float, double, string, unsigned int, SomeObject * ve daha fazlası olabilir.


1

Genişletilmiş bir birleştirme desteği eklemek için bir yardımcı başlık dosyası oluşturdum.

Aşağıdaki kodu genel başlık dosyanıza eklemeniz ve gerektiğinde eklemeniz yeterlidir.

Kullanım Örnekleri:

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

Sahnenin arkasındaki kod:

#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

İşte yapmanıza izin verecek genel bir C ++ 11 çözümü

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

Kod:

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

Aşağıdaki basit ve öğeleri dönüştürmek için pratik bir yoldur 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();
}

Buna gerek #include <sstream>için ostringstream.


1

@Sbi'nin, sınırlı olmayan genel bir çözümdekistd::vector<int> veya belirli bir dönüş dizesi türü üzerindeki girişimini genişletme . Aşağıda sunulan kod şu şekilde kullanılabilir:

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

Orijinal kodda, şablon bağımsız değişken kesintisi, ayırıcı bir dize değişmezi ise (yukarıdaki örneklerde olduğu gibi) doğru dönüş dizesi türünü üretmek için çalışmaz. Bu durumda, Str::value_typeişlev gövdesinde olduğu gibi yazılanlar yanlıştır. Kod, Strbunun her zaman benzer bir tür olduğunu varsayar std::basic_string, bu nedenle dize değişmezleri için açıkça başarısız olur.

Bunu düzeltmek için, aşağıdaki kod ayırıcı bağımsız değişkeninden yalnızca karakter türünü çıkarmaya çalışır ve bunu varsayılan bir dönüş dizesi türü üretmek için kullanır. Bu, boost::range_valueverilen aralık türünden öğe türünü çıkaran kullanılarak elde edilir .

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

Artık, basitçe yineleyici tabanlı aşırı yüklemeye ileten, aralık tabanlı bir aşırı yüklemeyi kolayca sağlayabiliriz:

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

Coliru'da Canlı Demo


0

@capone'un yaptığı gibi,

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

O zaman aşağıdaki gibi arayabiliriz:

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

tıpkı python gibi:

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

0

Bunun gibi bir şey kullanıyorum

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

@ Sbi'nin cevabıyla başladım, ancak çoğu zaman sonuçtaki dizgiyi bir akışa bağlayarak sona erdi, böylece bellekte tam diziyi oluşturma ek yükü olmadan bir akışa aktarılabilen aşağıdaki çözümü oluşturdu.

Aşağıdaki gibi kullanılır:

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

String_join.h ise:

#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

Aşağıdaki kodu yazdım. C # string.join tabanlıdır. Std :: string, std :: wstring ve birçok vektör türü ile çalışır. (yorumlardaki örnekler)

Şöyle diyelim:

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

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

Kod:

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

İşte tamsayılardan oluşan bir vektörü dizelere dönüştürmenin kolay bir yolu.

#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

şablon işlevini kullanarak katıl

Öğeleri template functionbirleştirmek için a kullandım vectorve gereksiz ififadeyi yalnızca içindeki sondan bir önceki öğeye kadar yineleyerek vector, ardından fordöngüden sonraki son öğeye katılarak kaldırdım . Bu aynı zamanda, birleştirilen dizinin sonundaki fazladan ayırıcıyı kaldırmak için fazladan kod ihtiyacını da ortadan kaldırır. Dolayısıyla, ifyinelemeyi yavaşlatan hiçbir ifade ve düzeltilmesi gereken gereksiz bir ayırıcı yok.

Bu katılmak için zarif bir işlev çağrısını üreten vectorOF string, integerya doublevs.

İki sürüm yazdım: biri bir dizge döndürür; diğeri doğrudan bir akıma yazar.

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

Çıktı

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.