Перетворити вектор <int> у рядок


92

У мене є vector<int>контейнер, який містить цілі числа (наприклад, {1,2,3,4}), і я хотів би перетворити його на рядок форми

"1,2,3,4"

Який найчистіший спосіб це зробити в С ++? У Python я б це зробив так:

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

Відповіді:


95

Безумовно, не такий елегантний, як Python, але ніщо так не елегантне, як Python в C ++.

Ви можете використовувати 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();

Ви також можете скористатися std::for_eachзамість цього.


Я думаю, ви маєте на увазі array.size (), а не v.size (), ні?
Марк Елліот

1
я, як би не називався вектор.
Brian R. Bondy

2
Це повинно бути std::string s = ss.str(). Якщо ви хочете const char*, скористайтеся s.c_str(). (Зауважте, що, хоча синтаксично правильним, ss.str().c_str()ви отримаєте a, const char*що вказує на тимчасове, яке перестане існувати в кінці повного виразу. Це болить.)
sbi

1
чому б просто не використовувати string.append?
Baiyan Huang

12
відповідь неповна без#include <sstream>
renadeen

43

Використовуючи std :: for_each та lambda, ви можете зробити щось цікаве.

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

Дивіться це запитання для невеликого класу, який я написав. Це не виведе кінцеву кому. Крім того, якщо ми припустимо, що C ++ 14 і надалі надаватиме нам еквіваленти алгоритмів на основі діапазону:

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
Я думаю, це не зовсім еквівалентно приєднанню Python - в кінці буде вставлено зайве ",".
1800 ІНФОРМАЦІЯ

2
Не рівнозначно, але настільки ж елегантно (насправді я думаю більше, але це лише думка).
Martin York,

20
Очевидно, що елегантність суб'єктивна. Тож якщо ви та ще двоє людей віддаєте перевагу довшому, більш повторюваному коду, який не працює, то він більш елегантний ;-p
Стів Джессоп,

1
Ви можете ігнорувати кінцеву кому, використовуючи функцію-член string :: substr. Призначте підрядку від 0 до n-1 для вашої змінної результату.
Дан

@SteveJessop: Краще?
Martin York

24

Ви можете використовувати std :: accumulate. Розглянемо наступний приклад

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

','повинно бути","
Метт

2
@Matt stringКлас має перевантаження для +оператора, який також може приймати символи. Так ','це просто добре.
Паван Манджунатх

19

Іншою альтернативою є використання std::copyта ostream_iteratorкласу:

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

Також не такий приємний, як Python. Для цього я створив joinфункцію:

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

Потім використовували його так:

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

Ви можете запитати, чому я здав ітератори. Ну, насправді я хотів змінити масив, тому використовував його так:

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

В ідеалі, я хотів би розробити шаблон до точки, де він може зробити висновок про тип char, і використовувати потокові рядки, але я ще не міг цього зрозуміти.


Оскільки для коментаря це було б занадто багато, я опублікував відповідь ( stackoverflow.com/questions/1430757/1432040#1432040 ), яка намагається розгадати загадку, наведену у вашому останньому реченні.
sbi

Вашу joinфункцію можна використовувати і з векторами? Будь ласка, наведіть приклад, я новачок у C ++.
Noitidart

Чи можете ви змінити ітератор на попередній приріст у відповіді?
Millie Smith

14

За допомогою Boost та C ++ 11 цього можна досягти так:

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

Ну, майже. Ось повний приклад:

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

Кредит Преторіану .

Ви можете обробляти будь-який тип значення наступним чином:

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

Це лише спроба розгадати загадку, висловлену зауваженням 1800 ІНФОРМАЦІЇ щодо його другого рішення, якому бракує загальності, а не спроба відповісти на питання:

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

Працює на моїй машині (TM).


Visual Studio 2013 дуже заплутаний в typedefs. Не те щоб ви могли це знати у 2009 році.
Граулт,

@Jes: Зараз я дивлюся на це протягом 5 хвилин, але не міг зрозуміти, про що може зіткнутися VS. Чи можете ви бути більш конкретними?
sbi

Вибачте, я думаю, що я спробував об’єднати об’єкти без << перевантажень, що зараз я вважаю невідповідним для вашого коду. Я не можу змусити ваш код не компілюватися з вектором рядків. До речі, VS 2013 Community є одночасно безкоштовною та повнофункціональною, на відміну від версій Express.
Граулт

@Jes: Це має працювати з будь-яким типом, який можна транслювати (тобто operator<<перевантажив). Звичайно, тип без operator<<може спричинити дуже заплутані повідомлення про помилки.
sbi

На жаль, це не компілюється: join(v.begin(), v.end(), ","). Виведення аргументу шаблону не дає правильного результату, якщо sepаргумент є рядковим літералом. Моя спроба вирішити це питання . Також забезпечується більш сучасне перевантаження на основі діапазону.
zett42,

7

Багато шаблонів / ідей. Моя не така загальна або ефективна, але у мене просто була та сама проблема, і я хотів вкинути це в суміш як щось коротке і солодке. Він виграє на найкоротшій кількості рядків ... :)

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
Дякуємо за спрощений код. Можливо, захочеться змінити його на "auto &", хоча, щоб уникнути зайвих копій та отримати легкий приріст продуктивності.
Millie Smith

Замість використання substr(...), використовуйте pop_back()для видалення останнього символу, стає набагато чіткішим і чистішим тоді.
ifyalciner

4

Якщо ви хочете зробити std::cout << join(myVector, ",") << std::endl;, ви можете зробити щось на зразок:

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

Зверніть увагу, що це рішення робить об’єднання безпосередньо у вихідний потік, а не створює вторинний буфер, і буде працювати з будь-якими типами, що мають оператор << для потоку.

Це також працює там, де boost::algorithm::join()не вдається, коли у вас є vector<char*>замість vector<string>.


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

7
Ласкаво просимо до Stack Overflow! Хоча цей код може вирішити проблему, найкраще додати детальніше роз’яснення та пояснити, як це працює для людей, які можуть не розуміти цей фрагмент коду.
paper1111

1
Поточна відповідь на вершину не набагато детальніша, і це найменша / найчистіша робоча відповідь. Не настільки ефективний, як std::stringstreamдля великих масивів, оскільки stringstreamзможе оптимістично розподілити пам’ять, що призведе до ефективності O (n.log (n)) замість O (n²) для масиву розміру nдля цієї відповіді. Також stringstreamне може створювати тимчасові рядки для to_string(i).
Aberaud

2

Мені подобається відповідь 1800 року. Однак я б перемістив першу ітерацію з циклу як результат оператора if лише один раз змінюється після першої ітерації

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

Звичайно, це можна звести до меншої кількості тверджень, якщо вам подобається:

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

Ви не повинні використовувати пост-збільшення для невідомих типів ітераторів. Це може коштувати дорого. (Звичайно, коли йдеться про струни, це може не мати великого значення. Але коли ви навчитеся звички ...)
sbi

Інкремент після публікації є нормальним, якщо ви використовуєте тимчасове значення, яке повертається. наприклад "result.append (* it); ++ it;" майже завжди настільки дорогий, як "result.append (* it ++);" друга має одну додаткову копію ітератора.
iain

На жаль, я щойно помітив збільшення посту у циклі for. помилка копіювання та вставки. Я зафіксував пост.
Iain,

1
@Ian: Коли я викладав C ++, я забивав своїх студентів використовувати ++iза винятком тих місць, де вони справді потребували, i++бо лише так вони не забували цього, коли це мало значення. (Те саме було зі мною, до речі.) Вони раніше вивчали Java, де всі види C-ізмів в моді, і це зайняло у них кілька місяців (1 лекція + лабораторна робота на тиждень), але в підсумку більшість вони навчились звички використовувати попереднє збільшення.
sbi

1
@sbi: погодився, я завжди за замовчуванням також попередньо збільшую, неправдивий постінкремент вийшов із копіювання когось іншого для циклу та його зміни. У своїй першій відповіді я думав, що вас турбує "result.append (* it ++)", а не цикл for. Мені було трохи соромно, коли я бачив збільшення посту у циклі. Здається, деякі люди дотримуються порад не використовувати занадто інкремент після публікації і ніколи не використовують його та не змінюють, навіть коли це доречно. Однак зараз я знаю, що ви не підпадаєте під цю категорію.
Iain,

2

Є кілька цікавих спроб надати елегантне вирішення проблеми. У мене була ідея використовувати шаблонні потоки, щоб ефективно відповісти на початкову дилему ОП. Хоча це стара публікація, я сподіваюся, що майбутні користувачі, які натраплять на це, знайдуть моє рішення вигідним.

По-перше, деякі відповіді (включаючи прийняту відповідь) не сприяють повторному використанню. Оскільки C ++ не забезпечує елегантного способу об'єднання рядків у стандартній бібліотеці (що я вже бачив), стає важливим створити гнучку та багаторазову. Ось мій постріл у цьому:

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

Тепер, щоб використовувати це, ви можете просто зробити щось на зразок наступного:

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

Зверніть увагу, як використання потоків робить це рішення неймовірно гнучким, оскільки ми можемо зберігати свій результат у рядку рядків, щоб повернути його пізніше, або ми можемо писати безпосередньо у стандартний вихід, файл або навіть у мережеве підключення, реалізоване як потік. Друкований тип повинен бути просто повторюваним та сумісним із вихідним потоком. STL забезпечує різні потоки, сумісні з широким спектром типів. Тож ви справді могли б з цим поїхати до міста. Зверху моєї голови, ваш вектор може бути int, float, double, string, unsigned int, SomeObject * тощо.


1

Я створив допоміжний файл заголовка, щоб додати розширену підтримку приєднання.

Просто додайте наведений нижче код до загального файлу заголовка та додайте його, коли це потрібно.

Приклади використання:

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

Код за кадром:

#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

Ось загальне рішення C ++ 11, яке дозволить вам це зробити

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

Код:

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 vectorв a string:

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

Вам потрібно #include <sstream>за ostringstream.


1

Розширення щодо спроби @sbi на загальне рішення, яке не обмежене std::vector<int>або певний тип рядка повернення. Код, представлений нижче, можна використовувати так:

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

В оригінальному коді виведення аргументу шаблону не працює, щоб отримати правильний тип рядка, що повертається, якщо роздільник є рядковим літералом (як у зразках вище). У цьому випадку typedefs, як Str::value_typeу тілі функції, є неправильним. Код передбачає, що Strце завжди тип типу std::basic_string, тому він, очевидно, не працює для рядкових літералів.

Щоб виправити це, наступний код намагається вивести лише тип символу з аргументу роздільника і використовує його для створення типового типу рядка, що повертається за замовчуванням. Це досягається за допомогою boost::range_value, яка витягує тип елемента із заданого типу діапазону .

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

Тепер ми можемо легко забезпечити перевантаження на основі діапазону, яка просто переходить до перевантаження на основі ітератора:

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


0

як це зробив @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 ;
}

Тоді ми можемо зателефонувати так:

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

так само, як python:

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

0

Я використовую щось подібне

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, але більшу частину часу закінчував підведенням отриманого рядка до потоку, таким чином створеного наведене нижче рішення, яке може бути передане до потоку без накладних витрат на створення повного рядка в пам'яті.

Він використовується наступним чином:

#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:

#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

Я написав наступний код. Він заснований на C # string.join. Він працює зі std :: string та std :: wstring та багатьма типами векторів. (приклади в коментарях)

Називайте це так:

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

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

Код:

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

Ось простий спосіб перетворити вектор цілих чисел у рядки.

#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

приєднатися за допомогою функції шаблону

Я використовував a, template functionщоб приєднати vectorелементи, і видалив непотрібний ifоператор, переглядаючи лише перші передостанні елементи в vector, а потім приєднуючи останній елемент після forциклу. Це також позбавляє потреби в додатковому коді для видалення зайвого роздільника в кінці приєднаного рядка. Отже, жодних ifтверджень, що уповільнюють ітерацію, і жодного зайвого роздільника, який потребує впорядкування.

Це виробляє виклик функції елегантного приєднатися до vectorOF string, integerабо doubleі т.д.

Я написав дві версії: одна повертає рядок; інший пише безпосередньо в потік.

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

Вихідні дані

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.