Подтвердить что ты не робот

Как взорвать вектор строк в строку (элегантный способ)

Я ищу самый элегантный способ вставить вектор строк в строку. Ниже приведено решение, которое я использую сейчас:

static std::string& implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
    for (std::vector<std::string>::const_iterator ii = elems.begin(); ii != elems.end(); ++ii)
    {
        s += (*ii);
        if ( ii + 1 != elems.end() ) {
            s += delim;
        }
    }

    return s;
}

static std::string implode(const std::vector<std::string>& elems, char delim)
{
    std::string s;
    return implode(elems, delim, s);
}

Есть ли там другие?

4b9b3361

Ответ 1

Это может быть не очевидно, но, добавляя строки в implode, вы делаете много распределений памяти и освобождений. Возможное улучшение - сделать reserve() для s один раз, а затем все ваши добавления.

Ответ 2

Используйте boost::algorithm::join(..):

#include <boost/algorithm/string/join.hpp>
...
std::string joinedString = boost::algorithm::join(elems, delim);

См. также этот вопрос.

Ответ 4

Вы должны использовать std::ostringstream вместо std::string для сборки вывода (тогда вы можете вызвать его метод str() в конце, чтобы получить строку, поэтому ваш интерфейс не должен меняться, а только временный s)).

Оттуда вы можете перейти к использованию std::ostream_iterator, например:

copy(elems.begin(), elems.end(), ostream_iterator<string>(s, delim)); 

Но это имеет две проблемы:

  • delim теперь должен быть const char*, а не один char. Нет большой сделки.
  • std::ostream_iterator записывает разделитель после каждого отдельного элемента, включая последний. Таким образом, вам нужно либо удалить последний в конце, либо написать собственную версию итератора, которая не имеет этой досады. Было бы полезно сделать последнее, если у вас много кода, который нуждается в таких вещах; в противном случае можно было бы избежать всего беспорядка (т.е. использовать ostringstream, но не ostream_iterator).

Ответ 5

Потому что я люблю однострочники (они очень полезны для всех видов странных вещей, как вы увидите в конце), здесь решение с использованием std:: accumulate и С++ 11 lambda:

std::accumulate(alist.begin(), alist.end(), std::string(), 
    [](const std::string& a, const std::string& b) -> std::string { 
        return a + (a.length() > 0 ? "," : "") + b; 
    } )

Я нахожу этот синтаксис полезным для оператора потока, где я не хочу, чтобы из потока выполнялись все виды странной логики вне сферы действия, просто для простого объединения строк. Рассмотрим, например, этот оператор return из метода, который форматирует строку с использованием операторов потока (с использованием std;):

return (dynamic_cast<ostringstream&>(ostringstream()
    << "List content: " << endl
    << std::accumulate(alist.begin(), alist.end(), std::string(), 
        [](const std::string& a, const std::string& b) -> std::string { 
            return a + (a.length() > 0 ? "," : "") + b; 
        } ) << endl
    << "Maybe some more stuff" << endl
    )).str();

Ответ 6

string join(const vector<string>& vec, const char* delim)
{
    stringstream res;
    copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
    return res.str();
}

Ответ 7

Версия, использующая std::accumulate:

#include <numeric>
#include <iostream>
#include <string>

struct infix {
  std::string sep;
  infix(const std::string& sep) : sep(sep) {}
  std::string operator()(const std::string& lhs, const std::string& rhs) {
    std::string rz(lhs);
    if(!lhs.empty() && !rhs.empty())
      rz += sep;
    rz += rhs;
    return rz;
  }
};

int main() {
  std::string a[] = { "Hello", "World", "is", "a", "program" };
  std::string sum = std::accumulate(a, a+5, std::string(), infix(", "));
  std::cout << sum << "\n";
}

Ответ 8

Специально с большими коллекциями вы хотите избежать проверки того, хотите ли вы добавить первый элемент или нет, чтобы не было трейлинг-разделителя...

Итак, для пустого или одноэлементного списка нет итерации вообще.

Пустые диапазоны тривиальны: return "".

Одиночный элемент или многоэлемент можно отлично обрабатывать с помощью accumulate:

auto join = [](const auto &&range, const auto separator) {
    if (range.empty()) return std::string();

    return std::accumulate(
         next(begin(range)), // there is at least 1 element, so OK.
         end(range),

         range[0], // the initial value

         [&separator](auto result, const auto &value) {
             return result + separator + value;
         });
};

Запуск образца (требуется С++ 14): http://cpp.sh/8uspd

Ответ 9

Вот еще один, который не добавляет разделитель после последнего элемента:

std::string concat_strings(const std::vector<std::string> &elements,
                           const std::string &separator)
{       
    if (!elements.empty())
    {
        std::stringstream ss;
        auto it = elements.cbegin();
        while (true)
        {
            ss << *it++;
            if (it != elements.cend())
                ss << separator;
            else
                return ss.str();
        }       
    }
    return "";

Ответ 10

как насчет простого глупого решения?

std::string String::join(const std::vector<std::string> &lst, const std::string &delim)
{
    std::string ret;
    for(const auto &s : lst) {
        if(!ret.empty())
            ret += delim;
        ret += s;
    }
    return ret;
}

Ответ 11

Использование части этого ответа на другой вопрос дает вам объединенное, основанное на разделителе без запятой,

Использование:

std::vector<std::string> input_str = std::vector<std::string>({"a", "b", "c"});
std::string result = string_join(input_str, ",");
printf("%s", result.c_str());
/// a,b,c

Код:

std::string string_join(const std::vector<std::string>& elements, const char* const separator)
{
    switch (elements.size())
    {
        case 0:
            return "";
        case 1:
            return elements[0];
        default:
            std::ostringstream os;
            std::copy(elements.begin(), elements.end() - 1, std::ostream_iterator<std::string>(os, separator));
            os << *elements.rbegin();
            return os.str();
    }
}

Ответ 12

Немного длинное решение, но не использует std::ostringstream и не требует взлома для удаления последнего разделителя.

http://www.ideone.com/hW1M9

И код:

struct appender
{
  appender(char d, std::string& sd, int ic) : delim(d), dest(sd), count(ic)
  {
    dest.reserve(2048);
  }

  void operator()(std::string const& copy)
  {
    dest.append(copy);
    if (--count)
      dest.append(1, delim);
  }

  char delim;
  mutable std::string& dest;
  mutable int count;
};

void implode(const std::vector<std::string>& elems, char delim, std::string& s)
{
  std::for_each(elems.begin(), elems.end(), appender(delim, s, elems.size()));
}

Ответ 13

просто добавьте!! Строка s = "";

for (int i = 0; i < doc.size(); i++)   //doc is the vector
    s += doc[i];

Ответ 14

Вот то, что я использую, просто и гибко

string joinList(vector<string> arr, string delimiter)
{
    if (arr.empty()) return "";

    string str;
    for (auto i : arr)
        str += i + delimiter;
    str = str.substr(0, str.size() - delimiter.size());
    return str;
}

с помощью:

string a = joinList({ "a", "bbb", "c" }, "[email protected]#");

выход:

[email protected]#[email protected]#c

Ответ 15

Во-первых, для этого требуется класс потока ostringstream для многократного конкатенации и сохранения основной проблемы избыточного выделения памяти.

код:

string join(const vector<string>& vec, const char* delim)
{
    ostringstream oss;
    if(!string_vector.empty()) {
        copy(string_vector.begin(),string_vector.end() - 1, ostream_iterator<string>(oss, delim.c_str()));
    }
    return oss.str();
}

vector<string> string_vector {"1", "2"};
string delim("->");
string joined_string = join();  // get "1->2"

Пояснение:

думая, лечите oss здесь как std::cout

когда мы хотим написать:

std::cout << string_vector[0] << "->" << string_vector[1] << "->",

мы можем использовать следующие классы STL в качестве справки:

ostream_iterator возвращает завернутый выходной поток с разделителями, автоматически добавляемыми каждый раз при использовании <<.

например,

ostream my_cout = ostream_iterator<string>(std::cout, "->")

wraps std:cout как my_cout

поэтому каждый раз, когда вы my_cout << "string_vector[0]",

это означает std::cout << "string_vector[0]" << "->"

Что касается copy(vector.begin(), vector.end(), std::out);

это означает std::cout << vector[0] << vector[1] (...) << vector[end]

Ответ 16

Мне нравится использовать это однострочное накопление (без конечного разделителя):

std::accumulate(
    std::next(elems.begin()), 
    elems.end(), 
    elems[0], 
    [](std::string a, std::string b) {
        return a + delimiter + b;
    }
);

Ответ 17

попробуйте это, но используя вектор вместо списка

template <class T>
std::string listToString(std::list<T> l){
    std::stringstream ss;
    for(std::list<int>::iterator it = l.begin(); it!=l.end(); ++it){
        ss << *it;
        if(std::distance(it,l.end())>1)
            ss << ", ";
    }
    return "[" + ss.str()+ "]";
}