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

Разделение строки символом

Я знаю, что это довольно простая проблема, но я просто хочу решить ее для себя раз и навсегда

Я просто хотел бы разбить строку на массив с использованием символа в качестве разделителя. (Очень похоже на известную функцию С# <.cplit(). Я могу, конечно, применить подход грубой силы, но мне интересно, есть ли что-то лучше этого.

До сих пор я искал и, вероятно, подход ближайший - это использование strtok(), однако из-за неудобства (преобразование вашей строки в char array и т.д.) Мне не нравится использовать его. Есть ли более простой способ реализовать это?

Примечание: Я хотел бы подчеркнуть это, потому что люди могут спросить: "Как это работает, грубая сила не работает". Моим решением грубой силы было создать цикл и использовать функцию substr() внутри. Однако, поскольку для нее требуется начальная точка и длина, она терпит неудачу, когда я хочу разделить дату. Поскольку пользователь может ввести его как 7/12/2012 или 07/3/2011, где я действительно могу рассказать длину, прежде чем вычислять следующее местоположение разделителя '/'.

4b9b3361

Ответ 1

Использование векторов, строк и потока строк. Немного громоздко, но это делает свое дело.

std::stringstream test("this_is_a_test_string");
std::string segment;
std::vector<std::string> seglist;

while(std::getline(test, segment, '_'))
{
   seglist.push_back(segment);
}

В результате получается вектор с тем же содержимым, что и

std::vector<std::string> seglist{ "this", "is", "a", "test", "string" };

Ответ 2

Другой способ (С++ 11/boost) для людей, которым нравится RegEx. Лично я большой поклонник RegEx для такого рода данных. IMO это намного мощнее, чем просто разделение строк с использованием разделителя, поскольку вы можете выбрать, чтобы быть намного умнее о том, что представляет собой "действительные" данные, если хотите.

#include <string>
#include <algorithm>    // copy
#include <iterator>     // back_inserter
#include <regex>        // regex, sregex_token_iterator
#include <vector>

int main()
{
    std::string str = "08/04/2012";
    std::vector<std::string> tokens;
    std::regex re("\\d+");

    //start/end points of tokens in str
    std::sregex_token_iterator
        begin(str.begin(), str.end(), re),
        end;

    std::copy(begin, end, std::back_inserter(tokens));
}

Ответ 3

У Boost есть split(), который вы ищете в algorithm/string.hpp:

std::string sample = "07/3/2011";
std::vector<string> strs;
boost::split(strs, sample, boost::is_any_of("/"));

Ответ 4

Другая возможность состоит в том, чтобы наполнить поток локалью, которая использует специальный грань ctype. Поток использует фасет ctype для определения того, что "пробел", который он рассматривает как разделители. С фазой ctype, которая классифицирует ваш разделительный символ как пробел, чтение может быть довольно тривиальным. Здесь один из способов реализовать грань:

struct field_reader: std::ctype<char> {

    field_reader(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table() {
        static std::vector<std::ctype_base::mask> 
            rc(table_size, std::ctype_base::mask());

        // we'll assume dates are either a/b/c or a-b-c:
        rc['/'] = std::ctype_base::space;
        rc['-'] = std::ctype_base::space;
        return &rc[0];
    }
};

Мы используем это, используя imbue, чтобы указать потоку использовать локаль, которая его включает, затем прочитайте данные из этого потока:

std::istringstream in("07/3/2011");
in.imbue(std::locale(std::locale(), new field_reader);

При этом расщепление становится почти тривиальным - просто инициализируйте вектор, используя пару istream_iterator, чтобы прочитать фрагменты из строки (встроенной в istringstream):

std::vector<std::string>((std::istream_iterator<std::string>(in),
                          std::istream_iterator<std::string>());

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

Ответ 5

Взгляните на boost:: tokenizer

Если вы хотите сворачивать свой собственный метод, вы можете использовать std::string::find(), чтобы определить точки расщепления.

Ответ 6

Мне не нравится stringstream, хотя я не уверен, почему. Сегодня я написал эту функцию, чтобы разрешить разбиение std::string на любой произвольный символ или строку на вектор. Я знаю, что этот вопрос старый, но я хотел бы поделиться альтернативным способом расщепления std::string.

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

#include <string>
#include <vector>

void split(std::string str, std::string splitBy, std::vector<std::string>& tokens)
{
    /* Store the original string in the array, so we can loop the rest
     * of the algorithm. */
    tokens.push_back(str);

    // Store the split index in a 'size_t' (unsigned integer) type.
    size_t splitAt;
    // Store the size of what we're splicing out.
    size_t splitLen = splitBy.size();
    // Create a string for temporarily storing the fragment we're processing.
    std::string frag;
    // Loop infinitely - break is internal.
    while(true)
    {
        /* Store the last string in the vector, which is the only logical
         * candidate for processing. */
        frag = tokens.back();
        /* The index where the split is. */
        splitAt = frag.find(splitBy);
        // If we didn't find a new split point...
        if(splitAt == string::npos)
        {
            // Break the loop and (implicitly) return.
            break;
        }
        /* Put everything from the left side of the split where the string
         * being processed used to be. */
        tokens.back() = frag.substr(0, splitAt);
        /* Push everything from the right side of the split to the next empty
         * index in the vector. */
        tokens.push_back(frag.substr(splitAt+splitLen, frag.size()-(splitAt+splitLen)));
    }
}

Чтобы использовать, просто вызовите так...

std::string foo = "This is some string I want to split by spaces.";
std::vector<std::string> results;
split(foo, " ", results);

Теперь вы можете получить доступ ко всем результатам в векторе. Просто, как это - нет stringstream, нет сторонних библиотек, не отбрасывается на C!

Ответ 7

Есть ли причина, по которой вы не хотите преобразовывать string в массив символов (char*)? Это довольно легко назвать .c_str(). Вы также можете использовать цикл и функцию .find().

строковый класс
строка .find()
строка .c_str()

Ответ 8

Как насчет функции erase()? Если вы знаете позицию exakt в строке, где нужно разделить, то вы можете "извлечь" поля в строке с помощью erase().

std::string date("01/02/2019");
std::string day(date);
std::string month(date);
std::string year(date);

day.erase(2, string::npos); // "01"
month.erase(0, 3).erase(2); // "02"
year.erase(0,6); // "2019"