Простой способ разбить последовательность строк, разделенных нулями, в C++

У меня есть ряд строк, хранящихся в одном массиве, разделенных нулями (например, ['f', 'o', 'o', '\0', 'b', 'a', 'r', '\0'...]), и мне нужно разделить это на std::vector<std::string> или похожие.

Я мог бы просто написать цикл из 10 строк, чтобы сделать это, используя std::find или же strlen (на самом деле я только что это сделал), но мне интересно, есть ли более простой / более изящный способ сделать это, например какой-то алгоритм STL, который я пропустил, который можно заставить это сделать.

Это довольно простая задача, и меня не удивит, если есть какая-то хитрая хитрость STL, которую можно применить, чтобы сделать ее еще проще.

Любой берущий?

7 ответов

Решение

Мои два цента:

const char* p = str;
std::vector<std::string> vector;

do {
  vector.push_back(std::string(p));
  p += vector.back().size() + 1;
} while ( // whatever condition applies );

Ускоренное решение:

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
//input_array must be a Range containing the input.
boost::split(
    strs,
    input_array,
    boost::is_any_of(boost::as_array("\0")));

Следующее опирается на std::string имея неявный конструктор, принимающий const char*, делая цикл очень простым двухслойным:

#include <iostream>
#include <string>
#include <vector>

template< std::size_t N >
std::vector<std::string> split_buffer(const char (&buf)[N])
{
    std::vector<std::string> result;

    for(const char* p=buf; p!=buf+sizeof(buf); p+=result.back().size()+1)
        result.push_back(p);

    return result;
}

int main()
{
    std::vector<std::string> test = split_buffer("wrgl\0brgl\0frgl\0srgl\0zrgl");

    for (auto it = test.begin(); it != test.end(); ++it)
        std::cout << '"' << *it << "\"\n";

    return 0;
}

Это решение предполагает, что размер буфера известен, и критерий конца списка строк. Если список заканчивается "\0\0" вместо этого условие в цикле должно быть изменено с p!=foo+sizeof(foo) в *p,

Более элегантное и актуальное решение (по сравнению с другим моим ответом) использует getline и сводится к 2 строкам только с C++2003, и ручное ведение и обработка циклов не требуется:

#include <iostream>
#include <sstream>
#include <string>

int main() {
    const char foo[] = "meh\0heh\0foo\0bar\0frob";

    std::istringstream ss (std::string(foo, foo + sizeof foo));
    std::string str;

    while (getline (ss, str, '\0'))
        std::cout << str << '\n';
}

Однако обратите внимание, что строковый конструктор, основанный на диапазоне, уже указывает на присущую проблему с расщеплением при -'\0: вы должны знать точный размер или найти какой-либо другой символ char-combo для Ultimate Terminator.

Вот решение, которое я придумал сам, предполагая, что буфер заканчивается сразу после последней строки:

std::vector<std::string> split(const std::vector<char>& buf) {
    auto cur = buf.begin();
    while (cur != buf.end()) {
        auto next = std::find(cur, buf.end(), '\0');
        drives.push_back(std::string(cur, next));
        cur = next + 1;
    }
    return drives;
}

Плохой ответ, на самом деле, но я усомнился в твоих утверждениях о 10-строчном цикле для ручного разбиения. 4 строки делают это для меня:

#include <vector>
#include <iostream>
int main() {
    using std::vector;

    const char foo[] = "meh\0heh\0foo\0bar\0frob";

    vector<vector<char> > strings(1);
    for (const char *it=foo, *end=foo+sizeof(foo); it!=end; ++it) {
        strings.back().push_back(*it);
        if (*it == '\0') strings.push_back(vector<char>());
    }

    std::cout << "number of strings: " << strings.size() << '\n';
    for (vector<vector<char> >::iterator it=strings.begin(), end=strings.end(); 
         it!=end; ++it)
        std::cout << it->data() << '\n';
}

В C string.h есть этот парень:

char * strtok ( char * str, const char * delimiters );

пример на cplusplus.com:

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

Это не C++, но это будет работать

Другие вопросы по тегам