Есть ли способ реализовать аналог «разделителя» Python.join() в C ?

#c #algorithm

#c #алгоритм

Вопрос:

Все, что я нашел, это boost::algorithm::string::join . Однако, кажется излишним использовать Boost только для join. Так, может быть, есть какие-то проверенные временем рецепты?

ОБНОВЛЕНИЕ:
Извините, заголовок вопроса был плохим. Я ищу метод для объединения строк с разделителем, а не просто для объединения по одному.

Ответ №1:

Поскольку вы ищете рецепт, продолжайте и используйте тот, что из Boost. Как только вы преодолеете всю общность, это не слишком сложно:

  1. Выделите место для хранения результата.
  2. Добавьте первый элемент последовательности к результату.
  3. Пока есть дополнительные элементы, добавьте разделитель и следующий элемент к результату.
  4. Верните результат.

Вот версия, которая работает с двумя итераторами (в отличие от версии Boost, которая работает с диапазоном.

 template <typename Iter>
std::string join(Iter begin, Iter end, std::string constamp; separator)
{
  std::ostringstream resu<
  if (begin != end)
    result << *begin  ;
  while (begin != end)
    result << separator << *begin  ;
  return result.str();
}
  

Комментарии:

1. Пожалуйста, учтите, что эта версия, хотя и очень универсальна, далеко не самая производительная. Итак, если вы будете выполнять много объединений, вы можете захотеть избежать использования ostringstream.

2. @smoothware что вы предлагаете в качестве более производительного решения?

3. @hannibal Не использует потоки, в них нет необходимости. Вскоре я проведу тест производительности.

4. @hannibal: Итак, я просто попробовал тривиальное решение, которое, как я думал, будет быстрее (добавить к строке результат to_string ), но оказалось, что это примерно в 2 раза медленнее, так что, полагаю, я ошибался в этом пункте, спасибо 😉 Поскольку нам действительно нужно форматирование, думаю, стоит использовать класс, который был создан для этого… Обратите внимание, здесь используются целые числа. Для конкатенации конкретных строк (без выполнения форматирования) намного быстрее просто добавить!

5. Еще один момент: рассмотрите возможность сначала выполнить итерацию по всем строкам и подсчитать общий размер для std::string::reserve() результата, прежде чем все объединять (предполагая, что вы объединяете строковые объекты или string_views и т.д.

Ответ №2:

Если вы действительно хотите ''.join() , вы можете использовать std::copy с std::ostream_iterator до std::stringstream .

 #include <algorithm> // for std::copy
#include <iterator>  // for std::ostream_iterator
#include <sstream>   // for std::stringstream

std::vector<int> values(); // initialize these
std::stringstream buffer;
std::copy(values.begin(), values.end(), std::ostream_iterator<int>(buffer));
  

Это приведет к вставке всех значений в buffer . Вы также можете указать пользовательский разделитель для std::ostream_iterator , но он будет добавлен в конце (это существенное отличие от join ). Если вам не нужен разделитель, это сделает именно то, что вы хотите.

Ответ №3:

проще говоря, где тип в контейнере является int:

 std::string s = std::accumulate(  v.begin(), v.end(), std::to_string(v[0]),
                     [](const std::stringamp; a, int b){
                           return a   ", "   std::to_string(b);
                     });
  

Ответ №4:

Это работает с C 17:

 template<class...T>
std::string join(const std::stringamp; sep, Tamp;amp;...strings) {
    if constexpr(sizeof...(T)) {
        auto t = ((strings   sep)   ...);
        return t.substr(0, t.size() - sep.size());    
    } else {
        return "";
    }
}

int main() {
    std::cout << join(",", "apple", "orange", "banana") << std::endl;
    std::cout << join(",") << std::endl;
}
  

Он должен выводить:

 apple,orange,banana
  

Ответ №5:

Если вы используете Qt в своем проекте, вы можете напрямую использовать join функцию QString (QString Reference), и она работает так, как ожидается от python. Несколько примеров:

 QStringList strList;
qDebug() << strList.join(" and ");
  

Результат: ""

 strList << "id = 1";
qDebug() << strList.join(" and ");
  

Результат: "id = 1"

 strList << "name = me";
qDebug() << strList.join(" and ");
  

Результат: "id = 1 and name = me"

Ответ №6:

Строки C реализованы эффективно.

 std::string s = s1   s2   s3;
  

Это могло бы быть быстрее:

 std::string str;
str.reserve(total_size_to_concat);

for (std::size_t i = 0; i < s.length(); i  )
{
  str.append(s[i], s[i].length());
}
  

Но это в основном то, что делает ваш компилятор с operator и минимумом оптимизации, за исключением того, что он угадывает размер для резервирования.
Не стесняйтесь. Взгляните на реализацию строк. 🙂

Комментарии:

1. Я бы поспорил, что для большого количества строк намного быстрее использовать stringstream .

2. @Space: Возможно, в C 03. В C 0x я считаю, что перемещение сделает оба одинаково эффективными.

3. @Xeo: Я так не думаю, потому что семантика перемещения не поможет поместить все данные в один непрерывный блок памяти.

4. @Space: В конечной строке это будет, нет?

5. @rubenvb: Я не знаю, как они это реализовали, но обычно они знают свое дело.

Ответ №7:

Вот другая версия, которую я нахожу более удобной в использовании:

 std::string join(std::initializer_list<std::string> initList, const std::stringamp; separator = "\")
{
    std::string s;
    for(const autoamp; i : initList)
    {
        if(s.empty())
        {
            s = i;
        }
        else
        {
            s  = separator   i;
        }
    }

    return s;
}
  

Затем вы можете вызвать это таким образом:

 join({"C:", "Program Files", "..."});
  

Ответ №8:

Просто еще одно простое решение:

 template<class T>
std::string str_join(const std::stringamp; delim, const Tamp; items)
{
    std::string s;

    for (const autoamp; item : items) {
        if (!s.empty()) {
            s  = delim;
        }
        s  = item;
    }

    return s;
}
  

Для тех, кто не любит begin() , end() в качестве аргументов и предпочитает просто весь контейнер. И для тех, кто не любит потоки строк и предпочитает некоторые operator std::string() const вместо этого.

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

 auto s1 = str_join(", ", std::vector<const char*>{"1","2","3"});

struct X
{
    operator std::string() const
    {
        return "X";
    }
};

auto s2 = str_join(":", std::vector<X>{{}, {}, {}});
  

Должен работать с C 11 и более поздними версиями.