Как внедрить вектор строк в строку (элегантный способ) - PullRequest
65 голосов
/ 16 апреля 2011

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

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

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

Ответы [ 15 ]

117 голосов
/ 13 июня 2011

Использование boost::algorithm::join(..):

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

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

24 голосов
/ 16 апреля 2011
std::vector<std::string> strings;

const char* const delim = ", ";

std::ostringstream imploded;
std::copy(strings.begin(), strings.end(),
           std::ostream_iterator<std::string>(imploded, delim));

(включая <string>, <vector>, <sstream> и <iterator>)

Если вы хотите иметь чистый конец (без конечного разделителя), посмотрите здесь

21 голосов
/ 16 апреля 2011

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

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

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

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

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

Поскольку я люблю однострочники (они очень полезны для всех странных вещей, как вы увидите в конце), вот решение, использующее std :: аккумулят и C ++ 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; 
    } )

Я нахожу этот синтаксис полезным для оператора потока, где я не хочу выводить все виды странной логики из области действия потока, просто для простого объединения строк.Рассмотрим, например, этот оператор возврата из метода, который форматирует строку с помощью потоковых операторов (используя 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();
8 голосов
/ 25 августа 2013
string join(const vector<string>& vec, const char* delim)
{
    stringstream res;
    copy(vec.begin(), vec.end(), ostream_iterator<string>(res, delim));
    return res.str();
}
4 голосов
/ 18 апреля 2011

Версия, которая использует 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";
}
3 голосов
/ 18 января 2018

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

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;
}
3 голосов
/ 13 сентября 2016

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

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

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

Один элемент или многоэлемент может отлично обрабатываться 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;
         });
};

Рабочий образец ( требуется C ++ 14 ): http://cpp.sh/8uspd

3 голосов
/ 03 сентября 2014

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

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 "";
2 голосов
/ 18 июня 2018

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

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

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