Как перебирать слова строки?

Я пытаюсь перебрать слова строки.

Можно предположить, что строка состоит из слов, разделенных пробелами.

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

Лучшее решение, которое у меня есть прямо сейчас:

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

using namespace std;

int main()
{
    string s = "Somewhere down the road";
    istringstream iss(s);

    do
    {
        string subs;
        iss >> subs;
        cout << "Substring: " << subs << endl;
    } while (iss);
}

Есть ли более элегантный способ сделать это?

Чувак ... Элегантность - это просто причудливый способ сказать в моей книге «эффективность, которая выглядит красиво». Не уклоняйтесь от использования функций C и быстрых методов для выполнения чего-либо только потому, что это не содержится в шаблоне;)

user19302 25.10.2008 13:04
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
pyon 29.09.2009 19:47

@nlaq, за исключением того, что вам пришлось бы преобразовать свой строковый объект с помощью c_str () и снова вернуться к строке, если вам все еще нужно, чтобы она была строкой, нет?

Aaron H. 15.02.2011 03:00

@Eduardo: это тоже неправильно ... вам нужно проверить iss между попыткой потоковой передачи другого значения и использованием этого значения, то есть string sub; while (iss >> sub) cout << "Substring: " << sub << '\n';

Tony Delroy 11.04.2012 06:24

Как насчет строкового токенизатора: cplusplus.com/reference/cstring/strtok

James Oravec 23.04.2013 03:39

Различные варианты в C++ для этого по умолчанию: cplusplus.com/faq/sequences/strings/split

hB0 31.10.2013 04:23

Элегантность - это нечто большее, чем просто высокая эффективность. Элегантные атрибуты включают небольшое количество строк и высокую разборчивость. ИМХО Elegance - это не показатель эффективности, а ремонтопригодности.

Matt 31.03.2017 16:22

Большинство ответов здесь в основном ориентированы на латынь. Во многих ответах предполагается, что один символ может использоваться как «пробел», даже если вопрос определяет разделитель как пробел. В Юникоде не менее 25 символов пробела. Но разделение слов - это не просто проблема пробелов. Например, в слоговом письме, таком как тибетское, разграничение слов является семантической, а не синтаксической проблемой. Поэтому использование пробелов для извлечения слов не подходит для многих языков.

Konchog 29.10.2018 15:08

Небольшое дополнение к вышесказанному. Вы можете добавить фасет языкового стандарта, который рассматривает пунктуацию как пробел, поэтому вам не нужно обрабатывать его отдельно. codereview.stackexchange.com/a/57467/507

Martin York 21.02.2019 00:26

Ваш исходный код более элегантен, чем ответы.

ttulinsky 16.02.2021 05:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3 114
10
2 246 530
78
Перейти к ответу Данный вопрос помечен как решенный

Ответы 78

В STL такого метода уже нет.

Однако вы можете использовать функцию C strtok(), используя член std::string::c_str(), или написать свою собственную. Вот пример кода, который я нашел после быстрого поиска в Google ("Разделение строки STL"):

void Tokenize(const string& str,
              vector<string>& tokens,
              const string& delimiters = " ")
{
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

Взято из: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

Если у вас есть вопросы по образцу кода, оставьте комментарий, и я объясню.

И то, что он не реализует typedef, называемый итератором, или не перегружает оператор <<, не означает, что это плохой код. Я довольно часто использую функции C. Например, printf и scanf оба быстрее, чем std::cin и std::cout (значительно), синтаксис fopen намного удобнее для двоичных типов, и они также имеют тенденцию создавать меньшие EXE.

Не продавайтесь по этой сделке «Элегантность важнее производительности».

Мне известны строковые функции C, и я также знаю о проблемах с производительностью (и то, и другое я отметил в своем вопросе). Однако для этого конкретного вопроса я ищу элегантное решение на C++.

Ashwin Nanjappa 25.10.2008 13:16

... и вы не хотите просто создавать объектно-ориентированную оболочку над функциями C, почему?

user19302 25.10.2008 13:42

@Nelson LaQuet: Дай угадаю: потому что strtok не реентерабелен?

paercebal 25.10.2008 13:52

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

graham.reeds 25.10.2008 15:54

@Nelson не передавайте Когда-либо string.c_str () в strtok! strtok удаляет входную строку (вставляет символы '\ 0' для замены каждого разделителя foudn), а c_str () возвращает неизменяемую строку.

Evan Teran 25.10.2008 22:19

char * ch = новый символ [str.size ()]; strcpy (ch, str.c_str ()); ... удалить [] ch; // проблема решена.

user19302 26.10.2008 03:20

@Nelson: Этот массив должен иметь размер str.size () + 1 в вашем последнем комментарии. Но я согласен с вашим тезисом, что глупо избегать функций C по «эстетическим» причинам.

j_random_hacker 24.08.2009 13:08

«Например, printf и scanf работают быстрее, чем cin и cout» только потому, что синхронизация включена по умолчанию.

paulm 12.05.2014 23:13
«В STL такого метода уже нет» - что не так со строковым find_first_of и использованием итераторов для запоминания позиций? Затем используйте substr для извлечения.
jww 26.09.2014 04:44

@paulm: Нет, медленность потоков C++ вызвана фасетами. Они по-прежнему медленнее, чем функции stdio.h, даже когда синхронизация отключена (и в строковых потоках, которые не могут синхронизироваться).

Ben Voigt 13.04.2015 02:55

Или вы можете использовать strsep() (хотя и не как портативный). Если вас не интересует более одного символа в качестве разделителя, другой ответ дает представление (getdelim()), но вы также можете перебирать строку с помощью strchr(). Или ... есть много способов, в зависимости от того, что вам нужно и что вам нужно.

Pryftan 13.06.2020 21:22

Это мой любимый способ перебора строки. Вы можете делать все, что хотите, за слово.

string line = "a line of text to iterate through";
string word;

istringstream iss(line, istringstream::in);

while( iss >> word )     
{
    // Do something on `word` here...
}

Можно ли объявить word как char?

abatishchev 26.06.2010 21:23

Извините, абатищев, C++ не моя сильная сторона. Но я полагаю, что было бы несложно добавить внутренний цикл для перебора каждого символа в каждом слове. Но сейчас я считаю, что текущий цикл зависит от пробелов для разделения слов. Если вы не знаете, что между каждым пробелом есть только один символ, и в этом случае вы можете просто преобразовать слово в char ... извините, я ничем не могу помочь, я имел в виду освежить свой C++

gnomed 01.07.2010 02:18

если вы объявите слово как char, он будет перебирать каждый непробельный символ. Достаточно просто попробовать: stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;

Wayne Werner 04.08.2010 22:03

Использование std::stringstream, как у вас, отлично работает и делает именно то, что вы хотели. Если вы просто ищете другой способ сделать что-то, вы можете использовать std::find() / std::find_first_of() и std::string::substr().

Вот пример:

#include <iostream>
#include <string>

int main()
{
    std::string s("Somewhere down the road");
    std::string::size_type prev_pos = 0, pos = 0;

    while( (pos = s.find(' ', pos)) != std::string::npos )
    {
        std::string substring( s.substr(prev_pos, pos-prev_pos) );

        std::cout << substring << '\n';

        prev_pos = ++pos;
    }

    std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
    std::cout << substring << '\n';

    return 0;
}

Это работает только для односимвольных разделителей. Простое изменение позволяет ему работать с несколькими символами: prev_pos = pos += delimiter.length();

David Doria 05.02.2016 17:48

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

string stringlist[10];
int count = 0;

for (int i = 0; i < sequence.length(); i++)
{
    if (sequence[i] == ' ')
    {
        stringlist[count] = sequence.substr(0, i);
        sequence.erase(0, i+1);
        i = 0;
        count++;
    }
    else if (i == sequence.length()-1)  // Last word
    {
        stringlist[count] = sequence.substr(0, i+1);
    }
}

Это некрасиво, но в целом (за исключением знаков препинания и множества других ошибок) оно работает!

У меня возникло желание +1 к этому ответу из-за его простого, читаемого кода (который, как я полагаю, неправильно натер элегантофил, отсюда -1), но затем я увидел, что вы выделили массив строк фиксированного размера для хранения токенов. Да ладно тебе, знать, который сломается в самый неподходящий момент! :)

j_random_hacker 24.08.2009 13:14

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

#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
    vector<string> result;
    if (delim.empty()) {
        result.push_back(s);
        return result;
    }
    string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = search(substart, s.end(), delim.begin(), delim.end());
        string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.push_back(temp);
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

int main() {
    const vector<string> words = split("So close no matter how far", " ");
    copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}

Конечно, у Boost есть split(), который работает частично таким же образом. И, если под «пробелом» вы действительно имеете в виду любой тип пробела, использование разделения Boost с is_any_of() отлично работает.

Наконец, решение, которое правильно обрабатывает пустые токены с обеих сторон строки

fmuecke 09.09.2015 23:38

Это похоже на вопрос Stack Overflow Как токенизировать строку в C++?.

#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main(int argc, char** argv)
{
    string text = "token  test\tstring";

    char_separator<char> sep(" \t");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const string& t : tokens)
    {
        cout << t << "." << endl;
    }
}

Материализует ли это копию всех токенов или сохраняет только начальную и конечную позиции текущего токена?

einpoklum 09.04.2018 22:47

Я использую это для разделения строки по разделителю. Первый помещает результаты в заранее созданный вектор, второй возвращает новый вектор.

#include <string>
#include <sstream>
#include <vector>
#include <iterator>

template <typename Out>
void split(const std::string &s, char delim, Out result) {
    std::istringstream iss(s);
    std::string item;
    while (std::getline(iss, item, delim)) {
        *result++ = item;
    }
}

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

Обратите внимание, что это решение не пропускает пустые токены, поэтому следующее найдет 4 элемента, один из которых пуст:

std::vector<std::string> x = split("one:two::three", ':');

элегантное решение, я всегда забываю об этой конкретной "строке", вы, я не верю, что она знает кавычки и escape-последовательности.

boskom 27.05.2010 17:32

@stijn: вы говорите, что split("one two three", ' '); возвращает вектор с 4 элементами? Не уверен, что это так, но попробую.

Evan Teran 09.11.2010 18:45

подождите, кажется, форматирование удалило некоторые пробелы (или я их забыл): я говорю о строке «один, два, три» с двумя пробелами между «два» и «три»

stijn 09.11.2010 21:54

Мне понравилось это решение, однако я обернул функцию в шаблон, изменив параметр шаблона vector std :: string на параметр. Что касается меня, я также использовал boost :: lexical_cast для указанного параметра шаблона в push_back.

Kit10 09.08.2012 23:30

Как я могу изменить его для работы с std :: wstring, std :: getline не будет работать правильно?

キキジキ 19.11.2012 13:09
std::getline является шаблоном, поэтому он может «просто работать», если не посмотреть en.cppreference.com/w/cpp/string/basic_string/getline, чтобы выяснить, как его настроить. Передачи символа wchar_t в качестве разделителя может быть достаточно для запуска правильного шаблона.
Evan Teran 19.11.2012 20:29

если вы включаете оптимизацию возвращаемого значения, не можете ли вы заставить функцию возвращать значение void?

Rozuur 10.07.2013 18:52

Чтобы не пропускать пустые токены, сделайте проверку empty(): if (!item.empty()) elems.push_back(item)

0x499602D2 10.11.2013 02:33

Как насчет разделителя, содержащего два символа, как ->?

herohuyongtao 26.12.2013 12:15

@herohuyongtao, это решение работает только для одиночных разделителей символов.

Evan Teran 27.12.2013 10:11

@Copperpot Как вы это сделали в шаблоне?

loop 13.01.2014 03:02

@EvanTeran Это может быть не разделение строки, а общее сомнение в вашем коде. Элементы, которые вы передаете в качестве аргумента ссылки и снова возвращаете ссылку. Я просто хотел знать, есть ли для этого какая-то причина?

duslabo 25.01.2014 21:27

@JeshwanthKumarNK, в этом нет необходимости, но он позволяет вам делать такие вещи, как передавать результат напрямую в функцию, подобную этой: f(split(s, d, v)), сохраняя при этом преимущество предварительно выделенного vector, если хотите.

Evan Teran 25.01.2014 21:50

Предупреждение: split ("one: two :: three", ':') и split ("one: two :: three:", ':') возвращают одно и то же значение.

dshin 09.09.2015 22:04

почти идеально: split(":abc:def:", ':'); возвращает только 3 элемента вместо 4!

fmuecke 09.09.2015 23:31

Для меня очень важна возможность установить максимальное количество возвращаемых элементов.

Jonny 29.10.2015 04:25

@Jonny, должно быть тривиально, просто добавьте дополнительное условие в цикл while, сравнивающее размер vector с максимальным. Примерно так: while (elems.size() < max_count && std::getline(ss, item, delim)) {

Evan Teran 29.10.2015 08:57

@ Джонни, понятно. Ваш ответ выглядит немного сложнее, чем необходимо. Если вы установите максимальное значение по умолчанию на что-то вроде size_t(-1), это будет фактически «бесконечность» (это самый большой размер, который может представить ваша система, поэтому у вас закончится оперативная память, прежде чем вы нажмете это). Тогда вы можете сделать условие таким же простым, как мой комментарий выше. Больше не нужно дважды проверять состояние потока, выполнять второе чтение и тому подобное. Просто предложение :-).

Evan Teran 29.10.2015 09:02

Возможно, вы ошибаетесь, но из-за этого вы можете потерять конец веревки. Ну, в основном я имитирую функцию взрыва php, или мне так кажется.

Jonny 29.10.2015 09:08

Попался. Мое решение будет останавливаться на max_count, пропуская оставшуюся часть строки (поскольку она нашла нужную сумму). Я думаю, вы ищете что-то, что всегда сделает последнюю часть остальной строки. У меня тоже есть такие функции: github.com/eteran/cpp-utilities/blob/master/string.h Некоторые специально разработаны для максимально точного соответствия функциям обработки строк в php :-)

Evan Teran 29.10.2015 09:21

Почему не return split(s, delim, std::vector<std::string>());?

Gabriel 29.10.2015 22:53

@ Габриэль, ты мог бы. Но я думаю, что когда это было написано (несколько лет назад), наличие именованной переменной более надежно поощряло NVRO. С семантикой перемещения C++ 11 разница может быть намного меньше.

Evan Teran 30.10.2015 06:16

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

Diedre 20.06.2017 19:07

Я действительно хотел бы, чтобы они добавили стандартный метод с этой подписью: vector<string> std::string::split(char delimiter = ' ');

doctorram 03.02.2018 01:26

@loop См. шаблонную реализацию в gitlab.com/tbeu/wcx_setfolderdate/blob/master/src/splitstrin‌ g.h.

tbeu 07.07.2019 23:56

@tbeu исправляет вашу ссылку: gitlab.com/tbeu/wcx_setfolderdate/-/blob/master/src/…

luizfls 20.03.2020 07:17

Возможное решение с использованием Boost:

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

Этот подход может быть даже быстрее, чем подход stringstream. И поскольку это общая шаблонная функция, ее можно использовать для разделения других типов строк (wchar и т. д. Или UTF-8) с использованием всех видов разделителей.

Подробнее см. документация.

Скорость здесь не имеет значения, поскольку оба эти случая намного медленнее, чем функция, подобная strtok.

Tom 01.03.2009 19:51

Это практично и достаточно быстро, если вы знаете, что строка будет содержать всего несколько токенов, но если она будет содержать много, вы сожжете тонну памяти (и времени) на рост вектора. Так что нет, это не быстрее, чем решение stringstream - по крайней мере, не для больших n, что является единственным случаем, когда скорость имеет значение.

j_random_hacker 24.08.2009 13:02

А для тех, у кого еще нет boost ... bcp для этого копирует более 1000 файлов :)

Roman Starkov 10.06.2010 00:12

Предупреждение, когда дана пустая строка (""), этот метод возвращает вектор, содержащий "" строку. Поэтому добавьте "if (! String_to_split.empty ())" перед разделением.

Offirmo 11.10.2011 17:10

@Ian Не все разработчики встроенных приложений используют boost.

ACK_stoverflow 31.01.2012 22:23

В любом случае @ACK_stoverflow - встроенные разработчики, использующие C++?

WDRust 25.02.2012 12:10
bcp 'это приводит к появлению библиотек, таких как MPL, который, как мне кажется, является действительно вряд ли нужно для разделения текста. Чувак, это ПИТА ...
Luis Machuca 20.03.2012 22:24

@j_random_hacker: "по крайней мере, не для большого n, это единственный случай, когда скорость имеет значение" - также для маленького n в цикле большого n ...

Tony Delroy 11.04.2012 06:29

@tuxSlayer: различные стандарты POSIX / XOPEN / UNIX также указывают strtok_r

Tony Delroy 11.04.2012 06:31

@TonyDelroy: Да, и похоже, что в msvc он называется strtok_s (что означает «безопасно? :)». Не слишком портативный ...

tuxSlayer 11.04.2012 12:05

@tuxSlayer: если вы предпочитаете написать свою собственную реализацию вместо пятистрочного #if / #else / #endif, тогда выбейте себя ....

Tony Delroy 06.06.2012 19:43

Используйте std :: string :: find (..) и std :: string :: substr (..), нет необходимости использовать ускорение.

Nils 03.07.2012 16:49

на самом деле в нашей компании нам не разрешено использовать Boost из соображений безопасности, да, я знаю, но костюмы решили.

AndersK 27.08.2012 10:20

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

GMasucci 22.05.2013 12:19

приятно, что он даже работает для вызова инфраструктуры повышения в xcode (проект iOS) в классе cpp

user2083364 21.08.2013 13:48

Мое личное мнение заключается в том, что C и C++ - это языки, не предназначенные для гибкости или быстрого вывода на рынок решений, использование Boost почти то же самое, что выбор языка более высокого уровня, который предлагает больше абстракции, для тех, кто выбирает Java, C# и т. д.. Потому что для тех, кто нас не волнует, что именно они делают под капотом. Использование Boost также означало бы, что мне пришлось бы сообщить своему клиенту, что я включаю стороннюю библиотеку. Спасибо, в любом случае. :)

Tiago 04.05.2015 13:55

Использование Boost похоже на использование сиденья Booster: нет, спасибо.

Andrew 27.05.2015 01:31

Может ли boost :: split действительно работать со строкой utf-8? Вы можете поделиться какой-нибудь документацией по этому поводу? Я пытаюсь разбить строку utf-8 на новые строки. Будет ли boost :: split работать правильно, если строка, которую я передаю, использует кодировку utf-8?

sajas 02.03.2016 15:58

@Andrew: any_of входит в стандартную библиотеку с 2011 года: en.cppreference.com/w/cpp/algorithm/all_any_none_of

graham.reeds 24.05.2017 13:56
Ответ принят как подходящий

Как бы то ни было, вот еще один способ извлечения токенов из входной строки, полагающийся только на стандартные средства библиотеки. Это пример мощи и элегантности дизайна STL.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

int main() {
    using namespace std;
    string sentence = "And I feel fine...";
    istringstream iss(sentence);
    copy(istream_iterator<string>(iss),
         istream_iterator<string>(),
         ostream_iterator<string>(cout, "\n"));
}

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

vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));

... или создайте vector напрямую:

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};

Можно ли для этого указать разделитель? Как например разбиение на запятые?

l3dx 06.08.2009 15:49

@ l3dx: похоже, что параметр "\ n" является разделителем. Этот код очень хорош, но я хотел бы узнать о нем побольше. Может быть, кто-нибудь сможет объяснить каждую строчку этого фрагмента?

Jonathan 11.12.2009 20:30

@Jonathan: \ n в этом случае не является разделителем, это разделитель для вывода в cout.

huy 03.02.2010 15:37

на основании этого: cplusplus.com/reference/algorithm/copy нет. Поведение пробелов - это функция istream_iterator. Изящнее было бы свернуть самостоятельно.

Wayne Werner 04.08.2010 21:59

@ graham.reeds, @ l3dx: Пожалуйста, не пишите другой анализатор CSV, который не может обрабатывать поля в кавычках: en.wikipedia.org/wiki/Comma-separated_values

Douglas 01.09.2010 13:30

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

SmallChess 10.01.2011 06:57

Людям, которые спрашивают, как это работает: эквивалентный код, использующий меньше STL, будет выглядеть как string token; istringstream iss(sentence); while (iss >> token) { cout << token; } или { tokens.push_back(token); }.

user470379 07.02.2011 08:11

Почему я получаю сообщение «ошибка C2664: 'std :: back_inserter': невозможно преобразовать параметр 1 из 'std :: vector <_Ty> (__cdecl *) (void)' в 'std :: vector <_Ty> &'» в VS2008 ?

szx 17.04.2011 14:22

Аргумент шаблона для back_inserter должен быть string, а не vector<string>. То есть это должен быть back_inserter<string>(tokens), а не back_inserter<vector<string>>(tokens).

Nawaz 27.05.2012 18:56

Взгляните на диапазоны, если вы заботитесь об элегантности с практической точки зрения (т.е. делаете больше с меньшим количеством кода): slideshare.net/rawwell/iteratorsmustgo

Alexei Sholik 17.10.2012 22:27

На самом деле, этот может отлично работает с другими разделителями (хотя делать некоторые из них несколько некрасиво). Вы создаете фасет ctype, который классифицирует желаемые разделители как пробелы, создаете языковой стандарт, содержащий этот фасет, а затем насыщаете поток строк этим языковым стандартом перед извлечением строк.

Jerry Coffin 20.12.2012 00:30

Основное назначение istream_iterator состоит в том, что он может анализировать int, float, double и т. д. Из istream: istream_iterator <double> выполняет достойную работу, считывая двойные значения, разделенные пробелом. С передним или особенно задним вставным устройством это отличное сочетание! :)

Oleg 11.01.2013 06:48
vector имеет ctor, который принимает начальный и конечный итераторы, поэтому нет необходимости в вызове копирования для вставки их в контейнер.
legends2k 13.01.2013 22:41

@Kinderchocolate «Предполагается, что строка состоит из слов, разделенных пробелом» - Хм, не похоже на плохое решение проблемы вопроса. «не масштабируемый и не обслуживаемый» - Ха, симпатичный.

Christian Rau 07.02.2013 19:08

@Nawaz Зачем это нужно? Вы вставляете std::vector<std::string>, а не std::string. Но опять же, в любом случае не должно быть явного аргумента шаблона (ну, не должно быть даже back_inserter или copy, но ладно).

Christian Rau 07.02.2013 19:12

@ChristianRau: О, ты прав; первый фрагмент кода меня, наверное, смутил. На самом деле я должен был сказать, что вам не нужно упоминать аргумент шаблона в std::back_inserter; Фактически, упоминание аргумента шаблона противоречит самой цели back_inserter.

Nawaz 07.02.2013 20:30

зачем вам использовать фигурные скобки в vector<string> tokens{istream_iterator<string>{iss}, istream_iterator<string>{}};, потому что иначе это выглядит как вызов функции?

stewart99 07.01.2014 09:06

Вопросы: 1. Почему istream_iterator должен останавливаться на пробелах? Для меня пробелы также являются частью строки; 2. почему это очень неэффективно?

ziyuang 22.04.2015 15:23

Элегантность в том, что нужно 5 включает в себя 3 строки (не считая using <namespace> и довольно загадочного кода для ... разделения строки? Боже мой.

Michael Trouw 22.04.2015 18:42

Мы также могли использовать STL для разделения строки.

Moiz Sajid 30.08.2015 14:31

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

noɥʇʎԀʎzɐɹƆ 07.07.2016 18:23

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

exilit 21.07.2016 23:40

@doorfly Единственное место, где нужны фигурные скобки, - это istream_iterator<string>{}, потому что в противном случае это можно было бы рассматривать как функцию.

Seppo Enarvi 28.02.2017 23:31

Если вы используете wstring и ваш код ломается, проверьте этот ответ, чтобы исправить использование istream_iterator с wchar_t: stackoverflow.com/a/20959347/3543437

kayleeFrye_onDeck 03.07.2018 23:44

@ l3dx Да. Вы можете добавить в поток специализированный локальный код, который будет содержать a, пробел (и все остальные символы, а не пробел). Тогда код будет работать точно так же. codereview.stackexchange.com/a/57467/507

Martin York 21.02.2019 00:30

Этот код действительно может использовать некоторые комментарии, чтобы объяснить, что такое цель каждого элемента. Типичный человек, задающий этот вопрос, в конечном итоге получит больше вопросов после прочтения этого, например какова цель пустого istream_iterator или почему в решении «создать вектор напрямую» так много скобок.

2088639 15.10.2019 00:17

Я не думаю, что в этом есть какая-то мощность или элегантность по сравнению с просто std::string::split(). Конечно же такого split в STL нет

tjysdsg 14.05.2020 15:00

Вы можете установить разделитель istringstream stackoverflow.com/a/21814768/1943599

Mellester 02.07.2020 20:44

Для тех, кому не нравится жертвовать всей эффективностью ради размера кода и видеть «эффективный» как тип элегантности, следующее должно стать приятным местом (и я думаю, что класс контейнера шаблона - потрясающе элегантное дополнение):

template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
              const std::string& delimiters = " ", bool trimEmpty = false)
{
   std::string::size_type pos, lastPos = 0, length = str.length();

   using value_type = typename ContainerT::value_type;
   using size_type  = typename ContainerT::size_type;

   while(lastPos < length + 1)
   {
      pos = str.find_first_of(delimiters, lastPos);
      if (pos == std::string::npos)
      {
         pos = length;
      }

      if (pos != lastPos || !trimEmpty)
         tokens.push_back(value_type(str.data()+lastPos,
               (size_type)pos-lastPos ));

      lastPos = pos + 1;
   }
}

Я обычно предпочитаю использовать типы std::vector<std::string> в качестве второго параметра (ContainerT) ... но list<> намного быстрее, чем vector<>, когда прямой доступ не требуется, и вы даже можете создать свой собственный строковый класс и использовать что-то вроде std::list<subString>, где subString не работает. делать любые копии для невероятного увеличения скорости.

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

Кроме того, он не возвращает (крайне неэффективно) результата, а скорее передает токены в качестве ссылки, что также позволяет вам создавать токены, используя несколько вызовов, если вы того пожелаете.

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

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

Я большой поклонник этого, но для g ++ (и, вероятно, хорошей практики) любой, кто использует это, захочет typedefs и typenames: typedef ContainerT Base; typedef typename Base::value_type ValueType; typedef typename ValueType::size_type SizeType; Затем, чтобы заменить value_type и size_types соответственно.

aws 29.11.2011 01:41

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

Wes Miller 17.08.2012 15:51

Ага, я понял это. Я поместил строки C++ из комментария aws в тело функции tokenize (), затем отредактировал строки tokens.push_back (), чтобы изменить ContainerT :: value_type только на ValueType и изменил (ContainerT :: value_type :: size_type) на ( SizeType). Исправлены биты, о которых скулит g ++. Просто вызовите его как tokenize (some_string, some_vector);

Wes Miller 17.08.2012 18:23

Может ли кто-нибудь быть настолько любезным, чтобы дать «краткое изложение» того, почему этот код имеет гораздо большую производительность?

user997112 14.10.2012 20:57

Помимо запуска нескольких тестов производительности на образцах данных, в первую очередь я сократил его до минимально возможного количества инструкций, а также до минимально возможного количества копий памяти, доступных за счет использования класса подстроки, который ссылается только на смещения / длины в других строках. (Я накатил свой, но есть и другие реализации). К сожалению, для улучшения этого мало что можно сделать, но постепенное увеличение было возможно.

Marius 29.11.2012 18:50

Может есть ошибка. Учитывая «xxxabcyyyabczzzabc» и «abo», результатом разделения будет «xxx | cyyy | czzz | c».

Guosheng 26.08.2015 12:23

Это правильный вывод, когда trimEmpty = true. Имейте в виду, что "abo" - это не разделитель в этом ответе, а список символов-разделителей. Было бы просто изменить его, чтобы он принимал одну строку символов-разделителей (я думаю, что str.find_first_of должен измениться на str.find_first, но я могу ошибаться ... не могу проверить)

Marius 28.08.2015 18:24

Спасибо @ thomas-perl за доработку, он действительно делает его более читаемым и компактным. Моя первоначальная реализация позволила избежать дополнительного сравнения на цикл, так как я оптимизировал приложение с очень малой задержкой. Однако ваше изменение будет более применимо для большинства пользователей, посещающих здесь.

Marius 27.09.2016 21:51

Сначала у меня были некоторые проблемы, но на самом деле это работает с wstring / unicode, если вы соответствующим образом обновите шаблон. Но будьте осторожны; Я столкнулся с некоторыми легко вызываемыми ошибками времени выполнения, которые компилятор не обнаружил в нескольких разных местах.

kayleeFrye_onDeck 08.06.2018 03:24

Спасибо @kayleeFrye_onDeck, я не использую C++ на этом уровне уже несколько лет и, возможно, немного устарел в новых спецификациях, но если есть что-то, что я должен исправить в этом посте, дайте мне знать, и я проверю это из.

Marius 27.07.2018 17:24

Вот еще один способ сделать это ..

void split_string(string text,vector<string>& words)
{
  int i=0;
  char ch;
  string word;

  while(ch=text[i++])
  {
    if (isspace(ch))
    {
      if (!word.empty())
      {
        words.push_back(word);
      }
      word = "";
    }
    else
    {
      word += ch;
    }
  }
  if (!word.empty())
  {
    words.push_back(word);
  }
}

Еще один гибкий и быстрый способ

template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
  const char* s = input;
  const char* e = s;
  while (*e != 0) {
    e = s;
    while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
    if (e - s > 0) {
      op(s, e - s);
    }
    s = e + 1;
  }
}

Чтобы использовать его с вектором строк (Edit: поскольку кто-то указал не наследовать классы STL ... hrmf;)):

template<class ContainerType>
class Appender {
public:
  Appender(ContainerType& container) : container_(container) {;}
  void operator() (const char* s, unsigned length) { 
    container_.push_back(std::string(s,length));
  }
private:
  ContainerType& container_;
};

std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");

Вот и все! И это всего лишь один из способов использования токенизатора, например, как просто подсчитать слова:

class WordCounter {
public:
  WordCounter() : noOfWords(0) {}
  void operator() (const char*, unsigned) {
    ++noOfWords;
  }
  unsigned noOfWords;
};

WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t"); 
ASSERT( wc.noOfWords == 7 );

Ограничено воображением;)

Хороший. По поводу Appender примечание «Почему бы нам не унаследовать класс от классов STL?»

Andreas Spindler 10.09.2013 16:07

Есть функция с именем strtok.

#include<string>
using namespace std;

vector<string> split(char* str,const char* delim)
{
    char* saveptr;
    char* token = strtok_r(str,delim,&saveptr);

    vector<string> result;

    while(token != NULL)
    {
        result.push_back(token);
        token = strtok_r(NULL,delim,&saveptr);
    }
    return result;
}
strtok взят из стандартной библиотеки C, а не C++. Использование в многопоточных программах небезопасно. Он изменяет входную строку.
Kevin Panko 14.06.2010 18:07

Поскольку он хранит указатель char от первого вызова в статической переменной, поэтому при последующих вызовах, когда передается NULL, он запоминает, какой указатель должен использоваться. Если второй поток вызывает strtok, когда другой поток все еще обрабатывает, этот указатель char будет перезаписан, и оба потока будут иметь неправильные результаты. mkssoftware.com/docs/man3/strtok.3.asp

Kevin Panko 14.06.2010 21:27

как упоминалось ранее, strtok небезопасен и даже в C рекомендуется использовать strtok_r

systemsfault 06.07.2010 16:17

strtok_r можно использовать, если вы находитесь в разделе кода, к которому можно получить доступ. это решение Только для всего вышеперечисленного, которое не является «линейным шумом» и является свидетельством того, что именно не так с C++

Erik Aronesty 10.10.2011 22:04

Обновлено, чтобы не было возражений по соображениям безопасности потоков от фанатов C++.

Erik Aronesty 02.05.2014 18:50

strtok - это зло. Он рассматривает два разделителя как один разделитель, если между ними ничего нет.

EvilTeach 11.08.2014 03:53

Цикл for () выглядит лучше. Как это davekb.com/browse_programming_tips:strtok_r_example:txt

Yetti99 09.06.2015 11:12

Я использую этот простак, потому что у нас есть класс String "специальный" (т.е. нестандартный):

void splitString(const String &s, const String &delim, std::vector<String> &result) {
    const int l = delim.length();
    int f = 0;
    int i = s.indexOf(delim,f);
    while (i>=0) {
        String token( i-f > 0 ? s.substring(f,i-f) : "");
        result.push_back(token);
        f=i+l;
        i = s.indexOf(delim,f);
    }
    String token = s.substring(f);
    result.push_back(token);
}

Цикл на getline с '' в качестве токена.

До сих пор я использовал один из Способствовать росту, но мне нужно было что-то, что не зависит от него, поэтому я пришел к следующему:

static void Split(std::vector<std::string>& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
    std::ostringstream word;
    for (size_t n = 0; n < input.size(); ++n)
    {
        if (std::string::npos == separators.find(input[n]))
            word << input[n];
        else
        {
            if (!word.str().empty() || !remove_empty)
                lst.push_back(word.str());
            word.str("");
        }
    }
    if (!word.str().empty() || !remove_empty)
        lst.push_back(word.str());
}

Хорошим моментом является то, что в separators вы можете передать более одного символа.

#include <vector>
#include <string>
#include <sstream>

int main()
{
    std::string str("Split me by whitespaces");
    std::string buf;                 // Have a buffer string
    std::stringstream ss(str);       // Insert the string into a stream

    std::vector<std::string> tokens; // Create vector to hold our words

    while (ss >> buf)
        tokens.push_back(buf);

    return 0;
}

Вы также можете разделить на другие разделители, если вы используете getline в условии while, например. для разделения запятыми используйте while(getline(ss, buff, ',')).

Ali 06.10.2018 23:20

Если вам нравится использовать ускорение, но вы хотите использовать целую строку в качестве разделителя (вместо отдельных символов, как в большинстве ранее предложенных решений), вы можете использовать boost_split_iterator.

Пример кода с удобным шаблоном:

#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>

template<typename _OutputIterator>
inline void split(
    const std::string& str, 
    const std::string& delim, 
    _OutputIterator result)
{
    using namespace boost::algorithm;
    typedef split_iterator<std::string::const_iterator> It;

    for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
            iter!=It();
            ++iter)
    {
        *(result++) = boost::copy_range<std::string>(*iter);
    }
}

int main(int argc, char* argv[])
{
    using namespace std;

    vector<string> splitted;
    split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));

    // or directly to console, for example
    split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
    return 0;
}

Следующее - гораздо лучший способ сделать это. Он может принимать любой символ и не разделяет строки, если вы этого не хотите. Никаких специальных библиотек (ну, кроме std, но кто действительно считает это дополнительной библиотекой), никаких указателей, никаких ссылок, и это статично. Просто простой C++.

#pragma once
#include <vector>
#include <sstream>
using namespace std;
class Helpers
{
    public:
        static vector<string> split(string s, char delim)
        {
            stringstream temp (stringstream::in | stringstream::out);
            vector<string> elems(0);
            if (s.size() == 0 || delim == 0)
                return elems;
            for(char c : s)
            {
                if (c == delim)
                {
                    elems.push_back(temp.str());
                    temp = stringstream(stringstream::in | stringstream::out);
                }
                else
                    temp << c;
            }
            if (temp.str().size() > 0)
                elems.push_back(temp.str());
                return elems;
            }

        //Splits string s with a list of delimiters in delims (it's just a list, like if we wanted to
        //split at the following letters, a, b, c we would make delims = "abc".
        static vector<string> split(string s, string delims)
        {
            stringstream temp (stringstream::in | stringstream::out);
            vector<string> elems(0);
            bool found;
            if (s.size() == 0 || delims.size() == 0)
                return elems;
            for(char c : s)
            {
                found = false;
                for(char d : delims)
                {
                    if (c == d)
                    {
                        elems.push_back(temp.str());
                        temp = stringstream(stringstream::in | stringstream::out);
                        found = true;
                        break;
                    }
                }
                if (!found)
                    temp << c;
            }
            if (temp.str().size() > 0)
                elems.push_back(temp.str());
            return elems;
        }
};

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

#include <iostream>
#include <string>
#include <boost/regex.hpp>

int main() {
    std::string line("A:::line::to:split");
    const boost::regex re(":+"); // one or more colons

    // -1 means find inverse matches aka split
    boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
    boost::sregex_token_iterator end;

    for (; tokens != end; ++tokens)
        std::cout << *tokens << std::endl;
}

Как насчет этого:

#include <string>
#include <vector>

using namespace std;

vector<string> split(string str, const char delim) {
    vector<string> v;
    string tmp;

    for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
        if (*i != delim && i != str.end()) {
            tmp += *i; 
        } else {
            v.push_back(tmp);
            tmp = ""; 
        }   
    }   

    return v;
}

Это лучший ответ здесь, если вы хотите разбить только на один символ-разделитель. Исходный вопрос хотел разделить на пробелы, что означает любую комбинацию одного или нескольких последовательных пробелов или вкладок. Вы действительно ответили stackoverflow.com/questions/53849

Oktalist 20.12.2012 02:09

Это моя версия, взятая из источника Кева:

#include <string>
#include <vector>
void split(vector<string> &result, string str, char delim ) {
  string tmp;
  string::iterator i;
  result.clear();

  for(i = str.begin(); i <= str.end(); ++i) {
    if ((const char)*i != delim  && i != str.end()) {
      tmp += *i;
    } else {
      result.push_back(tmp);
      tmp = "";
    }
  }
}

После этого вызовите функцию и сделайте с ней что-нибудь:

vector<string> hosts;
split(hosts, "192.168.1.2,192.168.1.3", ',');
for( size_t i = 0; i < hosts.size(); i++){
  cout <<  "Connecting host : " << hosts.at(i) << "..." << endl;
}

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

string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;

istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')

Вот еще одно решение. Он компактен и достаточно эффективен:

std::vector<std::string> split(const std::string &text, char sep) {
  std::vector<std::string> tokens;
  std::size_t start = 0, end = 0;
  while ((end = text.find(sep, start)) != std::string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = end + 1;
  }
  tokens.push_back(text.substr(start));
  return tokens;
}

Его можно легко шаблонизировать для обработки разделителей строк, широких строк и т. д.

Обратите внимание, что разделение "" приводит к одной пустой строке, а разделение "," (т.е. sep) приводит к двум пустым строкам.

Его также можно легко расширить, чтобы пропустить пустые токены:

std::vector<std::string> split(const std::string &text, char sep) {
    std::vector<std::string> tokens;
    std::size_t start = 0, end = 0;
    while ((end = text.find(sep, start)) != std::string::npos) {
        if (end != start) {
          tokens.push_back(text.substr(start, end - start));
        }
        start = end + 1;
    }
    if (end != start) {
       tokens.push_back(text.substr(start));
    }
    return tokens;
}

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

std::vector<std::string> split(const std::string& text, const std::string& delims)
{
    std::vector<std::string> tokens;
    std::size_t start = text.find_first_not_of(delims), end = 0;

    while((end = text.find_first_of(delims, start)) != std::string::npos)
    {
        tokens.push_back(text.substr(start, end - start));
        start = text.find_first_not_of(delims, end);
    }
    if (start != std::string::npos)
        tokens.push_back(text.substr(start));

    return tokens;
}

Первая версия проста и отлично справляется со своей задачей. Единственное изменение, которое я бы сделал, - это возвращать результат напрямую, а не передавать его в качестве параметра.

gregschlom 19.01.2012 06:25

Выходные данные передаются как параметр эффективности. Если результат будет возвращен, потребуется либо копия вектора, либо выделение кучи, которое затем необходимо будет освободить.

Alec Thomas 06.02.2012 22:56

Плохо, я ошибочно предполагал, что STL будет использовать ленивое копирование, как это делают контейнеры Qt. Жаль, что они этого не делают.

gregschlom 07.02.2012 02:58

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

Peter M 08.03.2013 00:04

Небольшое дополнение к моему комментарию выше: эта функция может возвращать вектор без штрафа, если используется семантика перемещения C++ 11.

Alec Thomas 27.06.2013 05:20

@AlecThomas: Даже до C++ 11 разве большинство компиляторов не оптимизировали бы обратную копию через NRVO? (+1 в любом случае; очень лаконично)

Marcelo Cantos 17.08.2013 15:54

@Peter M Я бы предпочел, чтобы это было передано по ссылке, на случай, если vector<string> станет большим.

Alex Spencer 15.11.2013 23:43

@Veritas Каким образом не работает, если разделитель - последний символ? Кроме того, вывод пустых токенов является преднамеренным, хотя его, очевидно, можно легко изменить, чтобы этого не делать, если это необходимо.

Alec Thomas 08.04.2014 19:36

Из всех ответов это один из самых привлекательных и гибких. Вместе с getline с разделителем, хотя это менее очевидное решение. В стандарте C++ 11 для этого нет ничего? Поддерживает ли C++ 11 перфокарты в наши дни?

Spacen Jasset 11.08.2015 18:15

Если вы передадите пустую строку, она вернет вектор с 1 элементом (пустая строка). Если вы передадите строку, аналогичную sep, она вернет вектор с двумя элементами (обе пустые строки). Для исправления этой проблемы необходимо иметь "if (end> 0) {" перед push_back в цикле while и "if (start> 0) {" перед push_back ниже цикла while.

LearnCocos2D 26.09.2015 20:12

@ LearnCocos2D Пожалуйста, не изменить смысл сообщения с редактированием. Такое поведение было задумано. Это идентично поведению оператора разделения Python. Я добавлю примечание, чтобы прояснить это.

Alec Thomas 28.09.2015 00:50

Предложите использовать std :: string :: size_type вместо int, поскольку в противном случае некоторые компиляторы могут выдавать подписанные / неподписанные предупреждения.

Pascal Kesseli 01.11.2015 23:45

первая функция в этом ответе - лучшее решение - отлично работает с функцией обратного соединения - std::string strJoin(const std::vector<std::string> v, const char& delimiter) { if (!v.empty()) { std::stringstream ss; std::string str(1, delimiter); auto it = v.cbegin(); while(true) { ss << *it++; if (it != v.cend()) ss << delimiter; else return ss.str(); } } return ""; }

Roman Shestakov 16.12.2017 18:45

Недавно мне пришлось разбить слово в верблюжьем регистре на подслова. Разделителей нет, только заглавные буквы.

#include <string>
#include <list>
#include <locale> // std::isupper

template<class String>
const std::list<String> split_camel_case_string(const String &s)
{
    std::list<String> R;
    String w;

    for (String::const_iterator i = s.begin(); i < s.end(); ++i) {  {
        if (std::isupper(*i)) {
            if (w.length()) {
                R.push_back(w);
                w.clear();
            }
        }
        w += *i;
    }

    if (w.length())
        R.push_back(w);
    return R;
}

Например, это разбивает «AQueryTrades» на «A», «Запрос» и «Сделки». Функция работает с узкими и широкими строками. Поскольку он уважает текущую местность, он разделяет "RaumfahrtÜberwachungsVerordnung" на "Raumfahrt", "Überwachungs" и "Verordnung".

Примечание. std::upper действительно следует передавать как аргумент шаблона функции. Затем более обобщенная функция этой функции может разбиваться на разделители, такие как ",", ";" или " ".

Было 2 ревизии. Это мило. Похоже, мой английский в значительной степени напоминал «немецкий». Однако ревизионист не исправил две незначительные ошибки, возможно, потому, что они и так были очевидны: в качестве аргумента можно было передать std::isupper, а не std::upper. Во-вторых, поставьте typename перед String::const_iterator.

Andreas Spindler 28.04.2015 10:20

В приведенном ниже коде используется strtok() для разделения строки на токены и сохранения токенов в векторе.

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

using namespace std;


char one_line_string[] = "hello hi how are you nice weather we are having ok then bye";
char seps[]   = " ,\t\n";
char *token;



int main()
{
   vector<string> vec_String_Lines;
   token = strtok( one_line_string, seps );

   cout << "Extracting and storing data in a vector..\n\n\n";

   while( token != NULL )
   {
      vec_String_Lines.push_back(token);
      token = strtok( NULL, seps );
   }
     cout << "Displaying end result in vector line storage..\n\n";

    for ( int i = 0; i < vec_String_Lines.size(); ++i)
    cout << vec_String_Lines[i] << "\n";
    cout << "\n\n\n";


return 0;
}

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

    // Split string into parts.
    class Split : public std::vector<std::string>
    {
        public:
            Split(const std::string& str, char* delimList)
            {
               size_t lastPos = 0;
               size_t pos = str.find_first_of(delimList);

               while (pos != std::string::npos)
               {
                    if (pos != lastPos)
                        push_back(str.substr(lastPos, pos-lastPos));
                    lastPos = pos + 1;
                    pos = str.find_first_of(delimList, lastPos);
               }
               if (lastPos < str.length())
                   push_back(str.substr(lastPos, pos-lastPos));
            }
    };

Пример, используемый для заполнения набора STL:

std::set<std::string> words;
Split split("Hello,World", ",");
words.insert(split.begin(), split.end());

Неэффективно, и вы используете контейнер STL - возможно, это одно из худших вещей, которые вы могли бы сделать.

Xander Tulip 19.03.2012 04:18

Я использую следующие

void split(string in, vector<string>& parts, char separator) {
    string::iterator  ts, curr;
    ts = curr = in.begin();
    for(; curr <= in.end(); curr++ ) {
        if ( (curr == in.end() || *curr == separator) && curr > ts )
               parts.push_back( string( ts, curr ));
        if ( curr == in.end() )
               break;
        if ( *curr == separator ) ts = curr + 1; 
    }
}

PlasmaHH, я забыл включить дополнительную проверку (curr> ts) на удаление токенов с пробелами.

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

PlasmaHH 08.03.2012 19:02

Вот функция разделения, которая:

  • общий
  • использует стандартный C++ (без повышения)
  • принимает несколько разделителей
  • игнорирует пустые токены (можно легко изменить)

    template<typename T>
    vector<T> 
    split(const T & str, const T & delimiters) {
        vector<T> v;
        typename T::size_type start = 0;
        auto pos = str.find_first_of(delimiters, start);
        while(pos != T::npos) {
            if (pos != start) // ignore empty tokens
                v.emplace_back(str, start, pos - start);
            start = pos + 1;
            pos = str.find_first_of(delimiters, start);
        }
        if (start < str.length()) // ignore trailing delimiter
            v.emplace_back(str, start, str.length() - start); // add what's left of the string
        return v;
    }
    

Пример использования:

    vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");

Вы забыли добавить в список использования: «крайне неэффективно»

Xander Tulip 19.03.2012 04:20

@XanderTulip, не могли бы вы быть более конструктивными и объяснить, как и почему?

Marco M. 21.03.2012 15:57

@XanderTulip: Я полагаю, вы имеете в виду, что он возвращает вектор по значению. Оптимизация возвращаемого значения (RVO, google it) должна позаботиться об этом. Также в C++ 11 вы можете вернуться по ссылке перемещения.

Joseph Garvin 07.05.2012 17:56

Фактически это может быть дополнительно оптимизировано: вместо .push_back (str.substr (...)) можно использовать .emplace_back (str, start, pos - start). Таким образом, строковый объект создается в контейнере, и, таким образом, мы избегаем операции перемещения + других махинаций, выполняемых функцией .substr.

Mihai Bişog 05.09.2012 17:50

@zoopp да. Хорошая идея. Когда я писал это, VS10 не имел поддержки emplace_back. Я обновлю свой ответ. Спасибо

Marco M. 12.09.2012 17:03

Может ли кто-нибудь вернуть его до максимального количества элементов N? Все оставшиеся символы должны оказаться в последнем элементе.

Jonny 29.10.2015 04:28

Кто-нибудь еще получает ошибку «отсутствует 'typename' перед именем зависимого типа 'T :: size_type'»?

Daniel Ryan 23.05.2017 06:06

Мой код:

#include <list>
#include <string>
template<class StringType = std::string, class ContainerType = std::list<StringType> >
class DSplitString:public ContainerType
{
public:
    explicit DSplitString(const StringType& strString, char cChar, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(cChar, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if ((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                push_back(strTemp);
            iPos = iPos_char + 1;
        }
    }
    explicit DSplitString(const StringType& strString, const StringType& strSub, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(strSub, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if ((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                push_back(strTemp);
            iPos = iPos_char + strSub.length();
        }
    }
};

Пример:

#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
    DSplitString<> aa("doicanhden1;doicanhden2;doicanhden3;", ';');
    for each (std::string var in aa)
    {
        std::cout << var << std::endl;
    }
    std::cin.get();
    return 0;
}

Это просто большая уродливая стена кода. Вы должны объяснить логику этого.

thecoshman 26.10.2012 12:25

Я использую следующий код:

namespace Core
{
    typedef std::wstring String;

    void SplitString(const Core::String& input, const Core::String& splitter, std::list<Core::String>& output)
    {
        if (splitter.empty())
        {
            throw std::invalid_argument(); // for example
        }

        std::list<Core::String> lines;

        Core::String::size_type offset = 0;

        for (;;)
        {
            Core::String::size_type splitterPos = input.find(splitter, offset);

            if (splitterPos != Core::String::npos)
            {
                lines.push_back(input.substr(offset, splitterPos - offset));
                offset = splitterPos + splitter.size();
            }
            else
            {
                lines.push_back(input.substr(offset));
                break;
            }
        }

        lines.swap(output);
    }
}

// gtest:

class SplitStringTest: public testing::Test
{
};

TEST_F(SplitStringTest, EmptyStringAndSplitter)
{
    std::list<Core::String> result;
    ASSERT_ANY_THROW(Core::SplitString(Core::String(), Core::String(), result));
}

TEST_F(SplitStringTest, NonEmptyStringAndEmptySplitter)
{
    std::list<Core::String> result;
    ASSERT_ANY_THROW(Core::SplitString(L"xy", Core::String(), result));
}

TEST_F(SplitStringTest, EmptyStringAndNonEmptySplitter)
{
    std::list<Core::String> result;
    Core::SplitString(Core::String(), Core::String(L","), result);
    ASSERT_EQ(1, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
}

TEST_F(SplitStringTest, OneCharSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L"x,y", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y", *result.rbegin());

    Core::SplitString(L",xy", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L"xy", *result.rbegin());

    Core::SplitString(L"xy,", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"xy", *result.begin());
    ASSERT_EQ(Core::String(), *result.rbegin());
}

TEST_F(SplitStringTest, TwoCharsSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L"x,.y,z", L",.", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y,z", *result.rbegin());

    Core::SplitString(L"x,,y,z", L",,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y,z", *result.rbegin());
}

TEST_F(SplitStringTest, RecursiveSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L",,,", L",,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L",", *result.rbegin());

    Core::SplitString(L",.,.,", L",.,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L".,", *result.rbegin());

    Core::SplitString(L"x,.,.,y", L",.,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L".,y", *result.rbegin());

    Core::SplitString(L",.,,.,", L",.,", result);
    ASSERT_EQ(3, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(Core::String(), *(++result.begin()));
    ASSERT_EQ(Core::String(), *result.rbegin());
}

TEST_F(SplitStringTest, NullTerminators)
{
    std::list<Core::String> result;

    Core::SplitString(L"xy", Core::String(L"\0", 1), result);
    ASSERT_EQ(1, result.size());
    ASSERT_EQ(L"xy", *result.begin());

    Core::SplitString(Core::String(L"x\0y", 3), Core::String(L"\0", 1), result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y", *result.rbegin());
}

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

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

StringSplitter.hpp:

#include <vector>
#include <iostream>
#include <string.h>

using namespace std;

class StringSplit
{
private:
    void copy_fragment(char*, char*, char*);
    void copy_fragment(char*, char*, char);
    bool match_fragment(char*, char*, int);
    int untilnextdelim(char*, char);
    int untilnextdelim(char*, char*);
    void assimilate(char*, char);
    void assimilate(char*, char*);
    bool string_contains(char*, char*);
    long calc_string_size(char*);
    void copy_string(char*, char*);

public:
    vector<char*> split_cstr(char);
    vector<char*> split_cstr(char*);
    vector<string> split_string(char);
    vector<string> split_string(char*);
    char* String;
    bool do_string;
    bool keep_empty;
    vector<char*> Container;
    vector<string> ContainerS;

    StringSplit(char * in)
    {
        String = in;
    }

    StringSplit(string in)
    {
        size_t len = calc_string_size((char*)in.c_str());
        String = new char[len + 1];
        memset(String, 0, len + 1);
        copy_string(String, (char*)in.c_str());
        do_string = true;
    }

    ~StringSplit()
    {
        for (int i = 0; i < Container.size(); i++)
        {
            if (Container[i] != NULL)
            {
                delete[] Container[i];
            }
        }
        if (do_string)
        {
            delete[] String;
        }
    }
};

StringSplitter.cpp:

#include <string.h>
#include <iostream>
#include <vector>
#include "StringSplit.hpp"

using namespace std;

void StringSplit::assimilate(char*src, char delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.push_back(x);
            }

        }
        else
        {
            delete[] temp;
        }
    }
}

void StringSplit::assimilate(char*src, char* delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.push_back(x);
            }
        }
        else
        {
            delete[] temp;
        }
    }
}

long StringSplit::calc_string_size(char* _in)
{
    long i = 0;
    while (*_in++)
    {
        i++;
    }
    return i;
}

bool StringSplit::string_contains(char* haystack, char* needle)
{
    size_t len = calc_string_size(needle);
    size_t lenh = calc_string_size(haystack);
    while (lenh--)
    {
        if (match_fragment(haystack + lenh, needle, len))
        {
            return true;
        }
    }
    return false;
}

bool StringSplit::match_fragment(char* _src, char* cmp, int len)
{
    while (len--)
    {
        if (*(_src + len) != *(cmp + len))
        {
            return false;
        }
    }
    return true;
}

int StringSplit::untilnextdelim(char* _in, char delim)
{
    size_t len = calc_string_size(_in);
    if (*_in == delim)
    {
        _in += 1;
        return len - 1;
    }

    int c = 0;
    while (*(_in + c) != delim && c < len)
    {
        c++;
    }

    return c;
}

int StringSplit::untilnextdelim(char* _in, char* delim)
{
    int s = calc_string_size(delim);
    int c = 1 + s;

    if (!string_contains(_in, delim))
    {
        return calc_string_size(_in);
    }
    else if (match_fragment(_in, delim, s))
    {
        _in += s;
        return calc_string_size(_in);
    }

    while (!match_fragment(_in + c, delim, s))
    {
        c++;
    }

    return c;
}

void StringSplit::copy_fragment(char* dest, char* src, char delim)
{
    if (*src == delim)
    {
        src++;
    }

    int c = 0;
    while (*(src + c) != delim && *(src + c))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

void StringSplit::copy_string(char* dest, char* src)
{
    int i = 0;
    while (*(src + i))
    {
        *(dest + i) = *(src + i);
        i++;
    }
}

void StringSplit::copy_fragment(char* dest, char* src, char* delim)
{
    size_t len = calc_string_size(delim);
    size_t lens = calc_string_size(src);

    if (match_fragment(src, delim, len))
    {
        src += len;
        lens -= len;
    }

    int c = 0;
    while (!match_fragment(src + c, delim, len) && (c < lens))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

vector<char*> StringSplit::split_cstr(char Delimiter)
{
    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char Delimiter)
{
    do_string = true;

    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

vector<char*> StringSplit::split_cstr(char* Delimiter)
{
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while(*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String,Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char* Delimiter)
{
    do_string = true;
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while (*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

Примеры:

int main(int argc, char*argv[])
{
    StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
    vector<char*> Split = ss.split_cstr(":CUT:");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

Выведет:

Это
is
an
example
cstring

int main(int argc, char*argv[])
{
    StringSplit ss = "This:is:an:example:cstring";
    vector<char*> Split = ss.split_cstr(':');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string("[SPLIT]");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This|is|an|example|string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string('|');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

Чтобы оставить пустые записи (по умолчанию пустые будут исключены):

StringSplit ss = mystring;
ss.keep_empty = true;
vector<string> Split = ss.split_string(":DELIM:");

Цель заключалась в том, чтобы сделать его похожим на метод C# Split (), в котором разделение строки выполняется так же просто, как:

String[] Split = 
    "Hey:cut:what's:cut:your:cut:name?".Split(new[]{":cut:"}, StringSplitOptions.None);

foreach(String X in Split)
{
    Console.Write(X);
}

Я надеюсь, что кому-то это будет так же полезно, как и мне.

У меня есть двухстрочное решение этой проблемы:

char sep = ' ';
std::string s = "1 This is an example";

for(size_t p=0, q=0; p!=s.npos; p=q)
  std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;

Тогда вместо печати вы можете поместить его в вектор.

Я написал эту функцию, которая во многом помогает мне. Это помогло мне при выполнении протокола для WebSockets.

using namespace std;
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

vector<string> split ( string input , string split_id ) {
  vector<string> result;
  int i = 0;
  bool add;
  string temp;
  stringstream ss;
  size_t found;
  string real;
  int r = 0;
    while ( i != input.length() ) {
        add = false;
        ss << input.at(i);
        temp = ss.str();
        found = temp.find(split_id);
        if ( found != string::npos ) {
            add = true;
            real.append ( temp , 0 , found );
        } else if ( r > 0 &&  ( i+1 ) == input.length() ) {
            add = true;
            real.append ( temp , 0 , found );
        }
        if ( add ) {
            result.push_back(real);
            ss.str(string());
            ss.clear();
            temp.clear();
            real.clear();
            r = 0;
        }
        i++;
        r++;
    }
  return result;
}

int main() {
    string s = "S,o,m,e,w,h,e,r,e, down the road \n In a really big C++ house.  \n  Lives a little old lady.   \n   That no one ever knew.    \n    She comes outside.     \n     In the very hot sun.      \n\n\n\n\n\n\n\n   And throws C++ at us.    \n    The End.  FIN.";
    vector < string > Token;
    Token = split ( s , "," );
    for ( int i = 0 ; i < Token.size(); i++)    cout << Token.at(i) << endl;
    cout << endl << Token.size();
    int a;
    cin >> a;
    return a;
}

Я написал следующий фрагмент кода. Вы можете указать разделитель, который может быть строкой. Результат аналогичен Java String.split с пустой строкой в ​​результате.

Например, если мы вызываем split («ABCPICKABCANYABCTWO: ABC», «ABC»), результат будет следующим:

0  <len:0>
1 PICK <len:4>
2 ANY <len:3>
3 TWO: <len:4>
4  <len:0>

Код:

vector <string> split(const string& str, const string& delimiter = " ") {
    vector <string> tokens;

    string::size_type lastPos = 0;
    string::size_type pos = str.find(delimiter, lastPos);

    while (string::npos != pos) {
        // Found a token, add it to the vector.
        cout << str.substr(lastPos, pos - lastPos) << endl;
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        lastPos = pos + delimiter.size();
        pos = str.find(delimiter, lastPos);
    }

    tokens.push_back(str.substr(lastPos, str.size() - lastPos));
    return tokens;
}

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

#include <regex.h>
#include <string.h>
#include <vector.h>

using namespace std;

vector<string> split(string s){
    regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
    regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
    regex_iterator<string::iterator> rend; //iterators to iterate thru words
    vector<string> result<regex_iterator>(rit, rend);
    return result;  //iterates through the matches to fill the vector
}

Подобные ответы с, возможно, лучшим подходом с регулярным выражением: здесь и здесь.

Brent Bradburn 06.12.2014 02:25
void splitString(string str, char delim, string array[], const int arraySize)
{
    int delimPosition, subStrSize, subStrStart = 0;

    for (int index = 0; delimPosition != -1; index++)
    {
        delimPosition = str.find(delim, subStrStart);
        subStrSize = delimPosition - subStrStart;
        array[index] = str.substr(subStrStart, subStrSize);
        subStrStart =+ (delimPosition + 1);
    }
}

Добро пожаловать в StackOverflow. Ваш ответ был бы улучшен, если бы вы описали код немного подробнее. Что отличает его от одного (очень высокого) ответа на этот старый вопрос?

marko 05.12.2012 03:20

Получите Способствовать росту! : -)

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace boost;

int main(int argc, char**argv) {
    typedef vector < string > list_type;

    list_type list;
    string line;

    line = "Somewhere down the road";
    split(list, line, is_any_of(" "));

    for(int i = 0; i < list.size(); i++)
    {
        cout << list[i] << endl;
    }

    return 0;
}

Этот пример дает результат -

Somewhere
down
the
road

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

template <class Container, class String, class Predicate>
void split(Container& output, const String& input,
           const Predicate& pred, bool trimEmpty = false) {
    auto it = begin(input);
    auto itLast = it;
    while (it = find_if (it, end(input), pred), it != end(input)) {
        if (not (trimEmpty and it == itLast)) {
            output.emplace_back(itLast, it);
        }
        ++it;
        itLast = it;
    }
}

Тогда вы можете использовать это так:

struct Delim {
    bool operator()(char c) {
        return not isalpha(c);
    }
};    

int main() {
    string s("#include<iostream>\n"
             "int main() { std::cout << \"Hello world!\" << std::endl; }");

    vector<string> v;

    split(v, s, Delim(), true);
    /* Which is also the same as */
    split(v, s, [](char c) { return not isalpha(c); }, true);

    for (const auto& i : v) {
        cout << i << endl;
    }
}

Я только что написал прекрасный пример того, как разбить char по символу, который затем помещает каждый массив символов (слова, разделенные вашим символом) в вектор. Для простоты я сделал векторную строку std.

Надеюсь, это поможет и будет вам доступно для чтения.

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

void push(std::vector<std::string> &WORDS, std::string &TMP){
    WORDS.push_back(TMP);
    TMP = "";
}
std::vector<std::string> mySplit(char STRING[]){
        std::vector<std::string> words;
        std::string s;
        for(unsigned short i = 0; i < strlen(STRING); i++){
            if (STRING[i] != ' '){
                s += STRING[i];
            }else{
                push(words, s);
            }
        }
        push(words, s);//Used to get last split
        return words;
}

int main(){
    char string[] = "My awesome string.";
    std::cout << mySplit(string)[2];
    std::cin.get();
    return 0;
}

Моя реализация может быть альтернативным решением:

std::vector<std::wstring> SplitString(const std::wstring & String, const std::wstring & Seperator)
{
    std::vector<std::wstring> Lines;
    size_t stSearchPos = 0;
    size_t stFoundPos;
    while (stSearchPos < String.size() - 1)
    {
        stFoundPos = String.find(Seperator, stSearchPos);
        stFoundPos = (stFoundPos == std::string::npos) ? String.size() : stFoundPos;
        Lines.push_back(String.substr(stSearchPos, stFoundPos - stSearchPos));
        stSearchPos = stFoundPos + Seperator.size();
    }
    return Lines;
}

Код теста:

std::wstring MyString(L"Part 1SEPsecond partSEPlast partSEPend");
std::vector<std::wstring> Parts = IniFile::SplitString(MyString, L"SEP");
std::wcout << L"The string: " << MyString << std::endl;
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}
std::wcout << std::endl;
MyString = L"this,time,a,comma separated,string";
std::wcout << L"The string: " << MyString << std::endl;
Parts = IniFile::SplitString(MyString, L",");
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}

Вывод тестового кода:

The string: Part 1SEPsecond partSEPlast partSEPend
Part 1<---
second part<---
last part<---
end<---

The string: this,time,a,comma separated,string
this<---
time<---
a<---
comma separated<---
string<---
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;

    vector<string> split(const string &s, char delim) {
        vector<string> elems;
        stringstream ss(s);
        string item;
        while (getline(ss, item, delim)) {
            elems.push_back(item);
        }
        return elems;
    }

int main() {

        vector<string> x = split("thi is an sample test",' ');
        unsigned int i;
        for(i=0;i<x.size();i++)
            cout<<i<<":"<<x[i]<<endl;
        return 0;
}

LazyStringSplitter:

#include <string>
#include <algorithm>
#include <unordered_set>

using namespace std;

class LazyStringSplitter
{
    string::const_iterator start, finish;
    unordered_set<char> chop;

public:

    // Empty Constructor
    explicit LazyStringSplitter()
    {}

    explicit LazyStringSplitter (const string cstr, const string delims)
        : start(cstr.begin())
        , finish(cstr.end())
        , chop(delims.begin(), delims.end())
    {}

    void operator () (const string cstr, const string delims)
    {
        chop.insert(delims.begin(), delims.end());
        start = cstr.begin();
        finish = cstr.end();
    }

    bool empty() const { return (start >= finish); }

    string next()
    {
        // return empty string
        // if ran out of characters
        if (empty())
            return string("");

        auto runner = find_if (start, finish, [&](char c) {
            return chop.count(c) == 1;
        });

        // construct next string
        string ret(start, runner);
        start = runner + 1;

        // Never return empty string
        // + tail recursion makes this method efficient
        return !ret.empty() ? ret : next();
    }
};
  • Я называю этот метод LazyStringSplitter по одной причине - он не разбивает строку за один раз.
  • По сути, он ведет себя как генератор Python.
  • Он предоставляет метод под названием next, который возвращает следующую строку, отделенную от исходной.
  • Я использовал unordered_set из C++ 11 STL, поэтому поиск разделителей выполняется намного быстрее
  • А вот как это работает

ТЕСТОВАЯ ПРОГРАММА

#include <iostream>
using namespace std;

int main()
{
    LazyStringSplitter splitter;

    // split at the characters ' ', '!', '.', ','
    splitter("This, is a string. And here is another string! Let's test and see how well this does.", " !.,");

    while (!splitter.empty())
        cout << splitter.next() << endl;
    return 0;
}

ВЫХОД

This
is
a
string
And
here
is
another
string
Let's
test
and
see
how
well
this
does

Следующий план по улучшению этого - реализовать методы begin и end, чтобы можно было делать что-то вроде:

vector<string> split_string(splitter.begin(), splitter.end());

Многие сомнительные детали реализации помимо этого ответа - единственный, который делает это лениво. Я действительно разочарован в мире C++ здесь. Что ж, streamiterator тоже делает то же самое, но тогда каждый помещает результат в vector <string>, убивая все преимущества ...

Slava 26.09.2015 22:25

Я свернул свой собственный с помощью strtok и использовал boost для разделения строки. Лучший метод, который я нашел, - это Библиотека C++ String Toolkit. Это невероятно гибко и быстро.

#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>

const char *whitespace  = " \t\r\n\f";
const char *whitespace_and_punctuation  = " \t\r\n\f;, = ";

int main()
{
    {   // normal parsing of a string into a vector of strings
        std::string s("Somewhere down the road");
        std::vector<std::string> result;
        if ( strtk::parse( s, whitespace, result ) )
        {
            for(size_t i = 0; i < result.size(); ++i )
                std::cout << result[i] << std::endl;
        }
    }

    {  // parsing a string into a vector of floats with other separators
        // besides spaces

        std::string s("3.0, 3.14; 4.0");
        std::vector<float> values;
        if ( strtk::parse( s, whitespace_and_punctuation, values ) )
        {
            for(size_t i = 0; i < values.size(); ++i )
                std::cout << values[i] << std::endl;
        }
    }

    {  // parsing a string into specific variables

        std::string s("angle = 45; radius = 9.9");
        std::string w1, w2;
        float v1, v2;
        if ( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
        {
            std::cout << "word " << w1 << ", value " << v1 << std::endl;
            std::cout << "word " << w2 << ", value " << v2 << std::endl;
        }
    }

    return 0;
}

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

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

Вот мой маленький алгоритм, использующий только STL:

//use like this
//std::vector<std::wstring> vec = Split<std::wstring> (L"Hello##world##!", L"##");

template <typename valueType>
static std::vector <valueType> Split (valueType text, const valueType& delimiter)
{
    std::vector <valueType> tokens;
    size_t pos = 0;
    valueType token;

    while ((pos = text.find(delimiter)) != valueType::npos) 
    {
        token = text.substr(0, pos);
        tokens.push_back (token);
        text.erase(0, pos + delimiter.length());
    }
    tokens.push_back (text);

    return tokens;
}

Насколько я тестировал, его можно использовать с разделителями любой длины и формы. Создайте экземпляр с типом строки или wstring.

Все, что делает алгоритм, - это ищет разделитель, получает часть строки, которая соответствует разделителю, удаляет разделитель и выполняет поиск снова, пока не перестанет находить его.

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

Я надеюсь, что это помогает.

это на самом деле неплохо. хотя я не думаю, что стирание - самый эффективный способ и (2) как насчет сохранения пустых токенов?

fmuecke 09.09.2015 23:20

Нет Boost, нет строковых потоков, только стандартная библиотека C, взаимодействующая с std::string и std::list: функции библиотеки C для легкого анализа, типы данных C++ для удобного управления памятью.

Пробелом считается любая комбинация символов новой строки, табуляции и пробелов. Набор пробельных символов устанавливается переменной wschars.

#include <string>
#include <list>
#include <iostream>
#include <cstring>

using namespace std;

const char *wschars = "\t\n ";

list<string> split(const string &str)
{
  const char *cstr = str.c_str();
  list<string> out;

  while (*cstr) {                     // while remaining string not empty
    size_t toklen;
    cstr += strspn(cstr, wschars);    // skip leading whitespace
    toklen = strcspn(cstr, wschars);  // figure out token length
    if (toklen)                       // if we have a token, add to list
      out.push_back(string(cstr, toklen));
    cstr += toklen;                   // skip over token
  }

  // ran out of string; return list

  return out;
}

int main(int argc, char **argv)
{
  list<string> li = split(argv[1]);
  for (list<string>::iterator i = li.begin(); i != li.end(); i++)
    cout << "{" << *i << "}" << endl;
  return 0;
}

Бег:

$ ./split ""
$ ./split "a"
{a}
$ ./split " a "
{a}
$ ./split " a b"
{a}
{b}
$ ./split " a b c"
{a}
{b}
{c}
$ ./split " a b c d  "
{a}
{b}
{c}
{d}

Хвостовая рекурсивная версия split (сама разделена на две функции). Прекращены все деструктивные манипуляции с переменными, кроме вставки строк в список!

void split_rec(const char *cstr, list<string> &li)
{
  if (*cstr) {
    const size_t leadsp = strspn(cstr, wschars);
    const size_t toklen = strcspn(cstr + leadsp, wschars);

    if (toklen)
      li.push_back(string(cstr + leadsp, toklen));

    split_rec(cstr + leadsp + toklen, li);
  }
}

list<string> split(const string &str)
{
  list<string> out;
  split_rec(str.c_str(), out);
  return out;
}

пожалуйста, используйте std :: vector вместо list

fmuecke 09.09.2015 23:16

@fmuecke В вопросе нет требования о конкретном представлении, используемом для частей строки, поэтому нет необходимости включать ваше предложение в ответ.

Kaz 11.09.2015 21:59

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

#include <regex>
#include <string>
#include <vector>

std::vector<string> Tokenize( const string str, const std::regex regex )
{
    using namespace std;

    std::vector<string> result;

    sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
    sregex_token_iterator reg_end;

    for ( ; it != reg_end; ++it ) {
        if ( !it->str().empty() ) //token could be empty:check
            result.emplace_back( it->str() );
    }

    return result;
}

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

Обычно я проверяю только разделение на пробелы и запятые, поэтому у меня также есть эта функция по умолчанию:

std::vector<string> TokenizeDefault( const string str )
{
    using namespace std;

    regex re( "[\\s,]+" );

    return Tokenize( str, re );
}

"[\\s,]+" проверяет наличие пробелов (\\s) и запятых (,).

Обратите внимание: если вы хотите разделить wstring вместо string,

  • поменять все std::regex на std::wregex
  • поменять все sregex_token_iterator на wsregex_token_iterator

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

Это был бы мой любимый ответ, но std :: regex не работает в GCC 4.8. Они сказали, что правильно реализовали это в GCC 4.9. Я все еще даю тебе свой +1

mchiasson 19.08.2014 16:27

Это мой фаворит с небольшими изменениями: вектор возвращается как ссылка, как вы сказали, и аргументы «str» и «regex» также передаются по ссылкам. Спасибо.

QuantumKarl 16.10.2015 18:06

Необработанные строки очень полезны при работе с шаблонами регулярных выражений. Таким образом, вам не нужно использовать escape-последовательности ... Вы можете просто использовать R"([\s,]+)".

Sam 17.02.2018 20:42

Вот мой подход, разрезанный и разделенный:

string cut (string& str, const string& del)
{
    string f = str;

    if (in.find_first_of(del) != string::npos)
    {
        f = str.substr(0,str.find_first_of(del));
        str = str.substr(str.find_first_of(del)+del.length());
    }

    return f;
}

vector<string> split (const string& in, const string& del = " ")
{
    vector<string> out();
    string t = in;

    while (t.length() > del.length())
        out.push_back(cut(t,del));

    return out;
}

Кстати, если я могу что-то сделать, чтобы оптимизировать это ...

// adapted from a "regular" csv parse
std::string stringIn = "my csv  is 10233478 NOTseparated by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
    if (stringIn[i] == " ") {
        commaSeparated.push_back("");
        commaCounter++;
    } else {
        commaSeparated.at(commaCounter) += stringIn[i];
    }
}

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

пустые строки сохраняются как отдельные элементы.

Вот моя версия

#include <vector>

inline std::vector<std::string> Split(const std::string &str, const std::string &delim = " ")
{
    std::vector<std::string> tokens;
    if (str.size() > 0)
    {
        if (delim.size() > 0)
        {
            std::string::size_type currPos = 0, prevPos = 0;
            while ((currPos = str.find(delim, prevPos)) != std::string::npos)
            {
                std::string item = str.substr(prevPos, currPos - prevPos);
                if (item.size() > 0)
                {
                    tokens.push_back(item);
                }
                prevPos = currPos + 1;
            }
            tokens.push_back(str.substr(prevPos));
        }
        else
        {
            tokens.push_back(str);
        }
    }
    return tokens;
}

Он работает с многосимвольными разделителями. Это предотвращает попадание пустых жетонов в ваши результаты. Он использует один заголовок. Он возвращает строку как один токен, если вы не указали разделитель. Он также возвращает пустой результат, если строка пуста. К сожалению, это неэффективно из-за огромной копии std::vectorПОКА НЕ, которую вы компилируете с использованием C++ 11, который должен использовать схему перемещения. В C++ 11 этот код должен быть быстрым.

Вот мое решение с использованием C++ 11 и STL. Он должен быть достаточно эффективным:

#include <vector>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>

std::vector<std::string> split(const std::string& s)
{
    std::vector<std::string> v;

    const auto end = s.end();
    auto to = s.begin();
    decltype(to) from;

    while((from = std::find_if (to, end,
        [](char c){ return !std::isspace(c); })) != end)
    {
        to = std::find_if (from, end, [](char c){ return std::isspace(c); });
        v.emplace_back(from, to);
    }

    return v;
}

int main()
{
    std::string s = "this is the string  to  split";

    auto v = split(s);

    for(auto&& s: v)
        std::cout << s << '\n';
}

Выход:

this
is
the
string
to
split

Это неплохо. Я чувствую, что код мог бы быть более ясным, например Неожиданно end - это не s.end().

Timmmm 16.05.2017 13:12

@Timmmm Из любопытства, что бы вы посоветовали для pos, end и done?

Galik 16.05.2017 13:25

Также вы можете сделать это немного проще с find_first_of и find_first_not_of.

Timmmm 16.05.2017 13:27

@Timmmm Что ж, мне не следует использовать ptr_fun, но использование std::isspace делает код более легко изменяемым для адаптации к различным языкам. При этом моя текущая рабочая версия использует find_first_of. Это делает его более эффективным и позволяет разбивать любой символ, а не только пробелы. Фактически, у меня также есть версия, которая также разбивается на предоставленную строку, в которой используется std::search (кажется, возможности этой функции многократны).

Galik 16.05.2017 13:37

Ага, переписал нравится. Спасибо за код!

Timmmm 16.05.2017 13:41

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

Galik 16.05.2017 13:48

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

Вот мои мысли:

Чтобы разбить слова на вектор строки с помощью последовательности разделителей:

template<class Container>
std::vector<std::string> split_by_delimiters(const std::string& input, const Container& delimiters)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if (current, end(input), not_in(delimiters));
        if (first == end(input)) break;
        auto last = find_if (first, end(input), is_in(delimiters));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

разделить другим способом, указав последовательность допустимых символов:

template<class Container>
std::vector<std::string> split_by_valid_chars(const std::string& input, const Container& valid_chars)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if (current, end(input), is_in(valid_chars));
        if (first == end(input)) break;
        auto last = find_if (first, end(input), not_in(valid_chars));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

is_in и not_in определяются следующим образом:

namespace detail {
    template<class Container>
    struct is_in {
        is_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) != end(_charset);
        }

        const Container& _charset;
    };

    template<class Container>
    struct not_in {
        not_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) == end(_charset);
        }

        const Container& _charset;
    };

}

template<class Container>
detail::not_in<Container> not_in(const Container& c)
{
    return detail::not_in<Container>(c);
}

template<class Container>
detail::is_in<Container> is_in(const Container& c)
{
    return detail::is_in<Container>(c);
}

Когда мы имеем дело с пробелами в качестве разделителя, очевидный ответ об использовании std::istream_iterator<T> уже дан и много проголосовало. Конечно, элементы могут быть разделены не пробелами, а некоторым разделителем. Я не нашел ответа, который просто переопределяет значение пробела, называемого разделителем, а затем использует традиционный подход.

Чтобы изменить то, какие потоки считают пробелами, вы просто измените std::locale потока, используя (std::istream::imbue()) с фасетом std::ctype<char> с собственным определением того, что означает пробел (это можно сделать и для std::ctype<wchar_t>, но на самом деле это немного отличается, потому что std::ctype<char> управляется таблицами, а std::ctype<wchar_t> управляется виртуальными функциями).

#include <iostream>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <locale>

struct whitespace_mask {
    std::ctype_base::mask mask_table[std::ctype<char>::table_size];
    whitespace_mask(std::string const& spaces) {
        std::ctype_base::mask* table = this->mask_table;
        std::ctype_base::mask const* tab
            = std::use_facet<std::ctype<char>>(std::locale()).table();
        for (std::size_t i(0); i != std::ctype<char>::table_size; ++i) {
            table[i] = tab[i] & ~std::ctype_base::space;
        }
        std::for_each(spaces.begin(), spaces.end(), [=](unsigned char c) {
            table[c] |= std::ctype_base::space;
        });
    }
};
class whitespace_facet
    : private whitespace_mask
    , public std::ctype<char> {
public:
    whitespace_facet(std::string const& spaces)
        : whitespace_mask(spaces)
        , std::ctype<char>(this->mask_table) {
    }
};

struct whitespace {
    std::string spaces;
    whitespace(std::string const& spaces): spaces(spaces) {}
};
std::istream& operator>>(std::istream& in, whitespace const& ws) {
    std::locale loc(in.getloc(), new whitespace_facet(ws.spaces));
    in.imbue(loc);
    return in;
}
// everything above would probably go into a utility library...

int main() {
    std::istringstream in("a, b, c, d, e");
    std::copy(std::istream_iterator<std::string>(in >> whitespace(", ")),
              std::istream_iterator<std::string>(),
              std::ostream_iterator<std::string>(std::cout, "\n"));

    std::istringstream pipes("a b c|  d |e     e");
    std::copy(std::istream_iterator<std::string>(pipes >> whitespace("|")),
              std::istream_iterator<std::string>(),
              std::ostream_iterator<std::string>(std::cout, "\n"));   
}

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

void f(std::istream& in) {
    std::istream pipes(in.rdbuf());
    pipes >> whitespace("|");
    std::istream comma(in.rdbuf());
    comma >> whitespace(",");

    std::string s0, s1;
    if (pipes >> s0 >> std::ws   // read up to first pipe and ignore sequence of pipes
        && comma >> s1 >> std::ws) { // read up to first comma and ignore commas
        // ...
    }
}

Спасибо, @Jairo Abdiel Toribio Cisneros. У меня это работает, но ваша функция возвращает какой-то пустой элемент. Итак, для возврата без пустого я отредактировал следующее:

std::vector<std::string> split(std::string str, const char* delim) {
    std::vector<std::string> v;
    std::string tmp;

    for(std::string::const_iterator i = str.begin(); i <= str.end(); ++i) {
        if (*i != *delim && i != str.end()) {
            tmp += *i;
        } else {
            if (tmp.length() > 0) {
                v.push_back(tmp);
            }
            tmp = "";
        }
    }

    return v;
}

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

std::string s = "one:two::three";
std::string delim = ":";
std::vector<std::string> vv = split(s, delim.c_str());

Мы можем использовать strtok в C++,

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char str[] = "Mickey M;12034;911416313;M;01a;9001;NULL;0;13;12;0;CPP,C;MSC,3D;FEND,BEND,SEC;";
    char *pch = strtok (str,";,");
    while (pch != NULL)
    {
        cout<<pch<<"\n";
        pch = strtok (NULL, ";,");
    }
    return 0;
}
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <vector>

int main() {
    using namespace std;
   int n=8;
    string sentence = "10 20 30 40 5 6 7 8";
    istringstream iss(sentence);

  vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));

     for(int i=0;i<n;i++){
        cout<<tokens.at(i);
     }


}

Как любитель, это первое решение, которое пришло мне в голову. Мне любопытно, почему я еще не видел здесь аналогичного решения, есть ли что-то в корне неправильное в том, как я это сделал?

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

std::vector<std::string> split(const std::string &s, const std::string &delims)
{
    std::vector<std::string> result;
    std::string::size_type pos = 0;
    while (std::string::npos != (pos = s.find_first_not_of(delims, pos))) {
        auto pos2 = s.find_first_of(delims, pos);
        result.emplace_back(s.substr(pos, std::string::npos == pos2 ? pos2 : pos2 - pos));
        pos = pos2;
    }
    return result;
}

int main()
{
    std::string text{"And then I said: \"I don't get it, why would you even do that!?\""};
    std::string delims{" :;\".,?!"};
    auto words = split(text, delims);
    std::cout << "\nSentence:\n  " << text << "\n\nWords:";
    for (const auto &w : words) {
        std::cout << "\n  " << w;
    }
    return 0;
}

http://cpp.sh/7wmzy

Вот моя запись:

template <typename Container, typename InputIter, typename ForwardIter>
Container
split(InputIter first, InputIter last,
      ForwardIter s_first, ForwardIter s_last)
{
    Container output;

    while (true) {
        auto pos = std::find_first_of(first, last, s_first, s_last);
        output.emplace_back(first, pos);
        if (pos == last) {
            break;
        }

        first = ++pos;
    }

    return output;
}

template <typename Output = std::vector<std::string>,
          typename Input = std::string,
          typename Delims = std::string>
Output
split(const Input& input, const Delims& delims = " ")
{
    using std::cbegin;
    using std::cend;
    return split<Output>(cbegin(input), cend(input),
                         cbegin(delims), cend(delims));
}

auto vec = split("Mary had a little lamb");

Первое определение - это универсальная функция в стиле STL, принимающая две пары итераторов. Вторая - это удобная функция, которая избавит вас от необходимости делать все begin() и end() самостоятельно. Вы также можете указать тип выходного контейнера в качестве параметра шаблона, например, если хотите использовать list.

Что делает его элегантным (IMO), так это то, что в отличие от большинства других ответов, он не ограничивается строками, но будет работать с любым STL-совместимым контейнером. Без каких-либо изменений в приведенном выше коде вы можете сказать:

using vec_of_vecs_t = std::vector<std::vector<int>>;

std::vector<int> v{1, 2, 0, 3, 4, 5, 0, 7, 8, 0, 9};
auto r = split<vec_of_vecs_t>(v, std::initializer_list<int>{0, 2});

который будет расщеплять вектор v на отдельные векторы каждый раз, когда встречается 0 или 2.

(Также есть дополнительный бонус в том, что со строками эта реализация быстрее, чем версии на основе strtok() и getline(), по крайней мере, в моей системе.)

если вы хотите разделить строку на несколько символов, вы можете использовать

#include<iostream>
#include<string>
#include<vector>
#include<iterator>
#include<sstream>
#include<string>

using namespace std;
void replaceOtherChars(string &input, vector<char> &dividers)
{
    const char divider = dividers.at(0);
    int replaceIndex = 0;
    vector<char>::iterator it_begin = dividers.begin()+1,
        it_end= dividers.end();
    for(;it_begin!=it_end;++it_begin)
    {
        replaceIndex = 0;
        while(true)
        {
            replaceIndex=input.find_first_of(*it_begin,replaceIndex);
            if (replaceIndex==-1)
                break;
            input.at(replaceIndex)=divider;
        }
    }
}
vector<string> split(string str, vector<char> chars, bool missEmptySpace =true )
{
    vector<string> result;
    const char divider = chars.at(0);
    replaceOtherChars(str,chars);
    stringstream stream;
    stream<<str;    
    string temp;
    while(getline(stream,temp,divider))
    {
        if (missEmptySpace && temp.empty())
            continue;
        result.push_back(temp);
    }
    return result;
}
int main()
{
    string str  = "milk, pigs.... hot-dogs ";
    vector<char> arr;
    arr.push_back(' '); arr.push_back(','); arr.push_back('.');
    vector<string> result = split(str,arr);
    vector<string>::iterator it_begin= result.begin(),
        it_end= result.end();
    for(;it_begin!=it_end;++it_begin)
    {
        cout<<*it_begin<<endl;
    }
return 0;
}

Это продолжение одного из лучших ответов. Теперь он поддерживает установку максимального количества возвращаемых элементов N. Последний бит строки окажется в N-м элементе. Параметр MAXELEMENTS является необязательным, если он установлен по умолчанию 0, он вернет количество элементов неограниченный. :-)

.час:

class Myneatclass {
public:
    static std::vector<std::string>& split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS = 0);
    static std::vector<std::string> split(const std::string &s, char delim, const size_t MAXELEMENTS = 0);
};

.cpp:

std::vector<std::string>& Myneatclass::split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
        if (MAXELEMENTS > 0 && !ss.eof() && elems.size() + 1 >= MAXELEMENTS) {
            std::getline(ss, item);
            elems.push_back(item);
            break;
        }
    }
    return elems;
}
std::vector<std::string> Myneatclass::split(const std::string &s, char delim, const size_t MAXELEMENTS) {
    std::vector<std::string> elems;
    split(s, delim, elems, MAXELEMENTS);
    return elems;
}

Коротко и элегантно

#include <vector>
#include <string>
using namespace std;

vector<string> split(string data, string token)
{
    vector<string> output;
    size_t pos = string::npos; // size_t to avoid improbable overflow
    do
    {
        pos = data.find(token);
        output.push_back(data.substr(0, pos));
        if (string::npos != pos)
            data = data.substr(pos + token.size());
    } while (string::npos != pos);
    return output;
}

может использовать любую строку в качестве разделителя, также может использоваться с двоичными данными (std :: string поддерживает двоичные данные, включая нули)

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

auto a = split("this!!is!!!example!string", "!!");

выход:

this
is
!example!string

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

Alessandro Teruzzi 01.08.2016 18:30
#include <iostream>
#include <vector>
using namespace std;

int main() {
  string str = "ABC AABCD CDDD RABC GHTTYU FR";
  str += " "; //dirty hack: adding extra space to the end
  vector<string> v;

  for (int i=0; i<(int)str.size(); i++) {
    int a, b;
    a = i;

    for (int j=i; j<(int)str.size(); j++) {
      if (str[j] == ' ') {
        b = j;
        i = j;
        break;
      }
    }
    v.push_back(str.substr(a, b-a));
  }

  for (int i=0; i<v.size(); i++) {
    cout<<v[i].size()<<" "<<v[i]<<endl;
  }
  return 0;
}

Просто для удобства:

template<class V, typename T>
bool in(const V &v, const T &el) {
    return std::find(v.begin(), v.end(), el) != v.end();
}

Фактическое разделение на основе нескольких разделителей:

std::vector<std::string> split(const std::string &s,
                               const std::vector<char> &delims) {
    std::vector<std::string> res;
    auto stuff = [&delims](char c) { return !in(delims, c); };
    auto space = [&delims](char c) { return in(delims, c); };
    auto first = std::find_if (s.begin(), s.end(), stuff);
    while (first != s.end()) {
        auto last = std::find_if (first, s.end(), space);
        res.push_back(std::string(first, last));
        first = std::find_if (last + 1, s.end(), stuff);
    }
    return res;
}

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

int main() {
    std::string s = "   aaa,  bb  cc ";
    for (auto el: split(s, {' ', ','}))
        std::cout << el << std::endl;
    return 0;
}

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

std::vector<size_t> str_pos(const std::string &search, const std::string &target)
{
    std::vector<size_t> founds;

    if (!search.empty())
    {
        size_t start_pos = 0;

        while (true)
        {
            size_t found_pos = target.find(search, start_pos);

            if (found_pos != std::string::npos)
            {
                size_t found = found_pos;

                founds.push_back(found);

                start_pos = (found_pos + 1);
            }
            else
            {
                break;
            }
        }
    }

    return founds;
}

std::string str_sub_index(size_t begin_index, size_t end_index, const std::string &target)
{
    std::string sub;

    size_t size = target.length();

    const char* copy = target.c_str();

    for(size_t i = begin_index; i <= end_index; i++)
    {
        if (i >= size)
        {
            break;
        }
        else
        {
            char c = copy[i];

            sub += c;
        }
    }

    return sub;
}

std::vector<std::string> str_split(const std::string &delimiter, const std::string &target)
{
    std::vector<std::string> splits;

    if (!delimiter.empty())
    {
        std::vector<size_t> founds = str_pos(delimiter, target);

        size_t founds_size = founds.size();

        if (founds_size > 0)
        {
            size_t search_len = delimiter.length();

            size_t begin_index = 0;

            for(int i = 0; i <= founds_size; i++)
            {
                std::string sub;

                if (i != founds_size)
                {
                    size_t pos  = founds.at(i);

                    sub = str_sub_index(begin_index, pos - 1, target);

                    begin_index = (pos + search_len);
                }
                else
                {
                    sub = str_sub_index(begin_index, (target.length() - 1), target);
                }

                splits.push_back(sub);
            }
        }
    }

    return splits;
}

Эти фрагменты состоят из трех функций. Плохая новость заключается в том, что для использования функции str_split вам понадобятся две другие функции. Да, это огромный кусок кода. Но хорошая новость в том, что эти две дополнительные функции могут работать независимо и иногда могут быть полезны ... :)

Проверял функцию в блоке main() вот так:

int main()
{
    std::string s = "Hello, world! We need to make the world a better place. Because your world is also my world, and our children's world.";

    std::vector<std::string> split = str_split("world", s);

    for(int i = 0; i < split.size(); i++)
    {
        std::cout << split[i] << std::endl;
    }
}

И это даст:

Hello, 
! We need to make the 
 a better place. Because your 
 is also my 
, and our children's 
.

Я считаю, что это не самый эффективный код, но, по крайней мере, он работает. Надеюсь, это поможет.

Это мое решение этой проблемы:

vector<string> get_tokens(string str) {
    vector<string> dt;
    stringstream ss;
    string tmp; 
    ss << str;
    for (size_t i; !ss.eof(); ++i) {
        ss >> tmp;
        dt.push_back(tmp);
    }
    return dt;
}

Эта функция возвращает вектор строк.

#include <iostream>
#include <regex>

using namespace std;

int main() {
   string s = "foo bar  baz";
   regex e("\\s+");
   regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
   regex_token_iterator<string::iterator> end;
   while (i != end)
      cout << " [" << *i++ << "]";
}

ИМО, это самое близкое к python re.split (). См. cplusplus.com для получения дополнительной информации о regex_token_iterator. -1 (4-й аргумент в ctor regex_token_iterator) - это несоответствующий раздел последовательности, использующий совпадение в качестве разделителя.

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

#include<iostream>
#include<vector>
#include<string>
#include<stdio.h>
using namespace std;
int main()
{
    char x = '\0';
    string s = "";
    vector<string> q;
    x = getchar();
    while(x != '\n')
    {
        if (x == ' ')
        {
            q.push_back(s);
            s = "";
            x = getchar();
            continue;
        }
        s = s + x;
        x = getchar();
    }
    q.push_back(s);
    for(int i = 0; i<q.size(); i++)
        cout<<q[i]<<" ";
    return 0;
}
  1. Не заботится о нескольких пробелах.
  2. Если за последним словом сразу не следует символ новой строки, он включает пробел между последним символом последнего слова и символом новой строки.

На основе Ответ галика я сделал это. В основном это здесь, поэтому мне не нужно писать снова и снова. Это безумие, что в C++ до сих пор нет встроенной функции разделения. Функции:

  • Должно быть очень быстро.
  • Легко понять (я думаю).
  • Объединяет пустые разделы.
  • Тривиально использовать несколько разделителей (например, "\r\n")
#include <string>
#include <vector>
#include <algorithm>

std::vector<std::string> split(const std::string& s, const std::string& delims)
{
    using namespace std;

    vector<string> v;

    // Start of an element.
    size_t elemStart = 0;

    // We start searching from the end of the previous element, which
    // initially is the start of the string.
    size_t elemEnd = 0;

    // Find the first non-delim, i.e. the start of an element, after the end of the previous element.
    while((elemStart = s.find_first_not_of(delims, elemEnd)) != string::npos)
    {
        // Find the first delem, i.e. the end of the element (or if this fails it is the end of the string).
        elemEnd = s.find_first_of(delims, elemStart);
        // Add it.
        v.emplace_back(s, elemStart, elemEnd == string::npos ? string::npos : elemEnd - elemStart);
    }
    // When there are no more non-spaces, we are done.

    return v;
}

моя общая реализация для string и u32string ~ с использованием подписи boost::algorithm::split.

template<typename CharT, typename UnaryPredicate>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           UnaryPredicate predicate)
{
    using ST = std::basic_string<CharT>;
    using std::swap;
    std::vector<ST> tmp_result;
    auto iter = s.cbegin(),
         end_iter = s.cend();
    while (true)
    {
        /**
         * edge case: empty str -> push an empty str and exit.
         */
        auto find_iter = find_if (iter, end_iter, predicate);
        tmp_result.emplace_back(iter, find_iter);
        if (find_iter == end_iter) { break; }
        iter = ++find_iter; 
    }
    swap(tmp_result, split_result);
}


template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const std::basic_string<CharT>& char_candidate)
{
    std::unordered_set<CharT> candidate_set(char_candidate.cbegin(),
                                            char_candidate.cend());
    auto predicate = [&candidate_set](const CharT& c) {
        return candidate_set.count(c) > 0U;
    };
    return split(split_result, s, predicate);
}

template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const CharT* literals)
{
    return split(split_result, s, std::basic_string<CharT>(literals));
}

Да, я просмотрел все 30 примеров.

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

#include <string>
#include <vector>

using namespace std;

vector<string> split(const string &str, const string &delim)
{   
    const auto delim_pos = str.find(delim);

    if (delim_pos == string::npos)
        return {str};

    vector<string> ret{str.substr(0, delim_pos)};
    auto tail = split(str.substr(delim_pos + delim.size(), string::npos), delim);

    ret.insert(ret.end(), tail.begin(), tail.end());

    return ret;
}

Возможно, не самая эффективная из реализаций, но это очень простое рекурсивное решение, использующее только <string> и <vector>.

Ах, он написан на C++ 11, но в этом коде нет ничего особенного, поэтому вы можете легко адаптировать его к C++ 98.

Не то чтобы нам нужно больше ответов, но это то, что я придумал после того, как меня вдохновил Эван Теран.

std::vector <std::string> split(const string &input, auto delimiter, bool skipEmpty=true) {
  /*
  Splits a string at each delimiter and returns these strings as a string vector.
  If the delimiter is not found then nothing is returned.
  If skipEmpty is true then strings between delimiters that are 0 in length will be skipped.
  */
  bool delimiterFound = false;
  int pos=0, pPos=0;
  std::vector <std::string> result;
  while (true) {
    pos = input.find(delimiter,pPos);
    if (pos != std::string::npos) {
      if (skipEmpty==false or pos-pPos > 0) // if empty values are to be kept or not
        result.push_back(input.substr(pPos,pos-pPos));
      delimiterFound = true;
    } else {
      if (pPos < input.length() and delimiterFound) {
        if (skipEmpty==false or input.length()-pPos > 0) // if empty values are to be kept or not
          result.push_back(input.substr(pPos,input.length()-pPos));
      }
      break;
    }
    pPos = pos+1;
  }
  return result;
}
#include <iostream>
#include <string>
#include <deque>

std::deque<std::string> split(
    const std::string& line, 
    std::string::value_type delimiter,
    bool skipEmpty = false
) {
    std::deque<std::string> parts{};

    if (!skipEmpty && !line.empty() && delimiter == line.at(0)) {
        parts.push_back({});
    }

    for (const std::string::value_type& c : line) {
        if (
            (
                c == delimiter 
                &&
                (skipEmpty ? (!parts.empty() && !parts.back().empty()) : true)
            )
            ||
            (c != delimiter && parts.empty())
        ) {
            parts.push_back({});
        }

        if (c != delimiter) {
            parts.back().push_back(c);
        }
    }

    if (skipEmpty && !parts.empty() && parts.back().empty()) {
        parts.pop_back();
    }

    return parts;
}

void test(const std::string& line) {
    std::cout << line << std::endl;

    std::cout << "skipEmpty=0 |";
    for (const std::string& part : split(line, ':')) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << "skipEmpty=1 |";
    for (const std::string& part : split(line, ':', true)) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << std::endl;
}

int main() {
    test("foo:bar:::baz");
    test("");
    test("foo");
    test(":");
    test("::");
    test(":foo");
    test("::foo");
    test(":foo:");
    test(":foo::");

    return 0;
}

Выход:

foo:bar:::baz
skipEmpty=0 |foo|bar|||baz|
skipEmpty=1 |foo|bar|baz|


skipEmpty=0 |
skipEmpty=1 |

foo
skipEmpty=0 |foo|
skipEmpty=1 |foo|

:
skipEmpty=0 |||
skipEmpty=1 |

::
skipEmpty=0 ||||
skipEmpty=1 |

:foo
skipEmpty=0 ||foo|
skipEmpty=1 |foo|

::foo
skipEmpty=0 |||foo|
skipEmpty=1 |foo|

:foo:
skipEmpty=0 ||foo||
skipEmpty=1 |foo|

:foo::
skipEmpty=0 ||foo|||
skipEmpty=1 |foo|

Было бы полезно добавить некоторые пояснения.

Oomph Fortuity 24.11.2017 15:46

Этот ответ берет строку и помещает ее в вектор строк. Он использует библиотеку ускорения.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

Используя std::string_view и библиотеку range-v3 Эрика Ниблера:

https://wandbox.org/permlink/kW5lwRCL1pxjp2pW

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"

int main() {
    std::string s = "Somewhere down the range v3 library";
    ranges::for_each(s  
        |   ranges::view::split(' ')
        |   ranges::view::transform([](auto &&sub) {
                return std::string_view(&*sub.begin(), ranges::distance(sub));
            }),
        [](auto s) {std::cout << "Substring: " << s << "\n";}
    );
}

Используя цикл диапазона for вместо алгоритма ranges::for_each:

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"

int main()
{
    std::string str = "Somewhere down the range v3 library";
    for (auto s : str | ranges::view::split(' ')
                      | ranges::view::transform([](auto&& sub) { return std::string_view(&*sub.begin(), ranges::distance(sub)); }
                      ))
    {
        std::cout << "Substring: " << s << "\n";
    }
}

Yepp, ассортимент на основе выглядит лучше - согласен

Porsche9II 17.05.2019 11:57

У меня совсем другой подход, чем у других решений, который предлагает большую ценность, чего не хватает другим решениям, но, конечно, также имеет свои недостатки. Здесь - это рабочая реализация с примером помещения <tag></tag> вокруг слов.

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

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

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

using namespace std;

#include <iostream>
#include <string>

#include <cctype>

typedef enum boundary_type_e {
    E_BOUNDARY_TYPE_ERROR = -1,
    E_BOUNDARY_TYPE_NONE,
    E_BOUNDARY_TYPE_LEFT,
    E_BOUNDARY_TYPE_RIGHT,
} boundary_type_t;

typedef struct boundary_s {
    boundary_type_t type;
    int pos;
} boundary_t;

bool is_delim_char(int c) {
    return isspace(c); // also compare against any other chars you want to use as delimiters
}

bool is_word_char(int c) {
    return ' ' <= c && c <= '~' && !is_delim_char(c);
}

boundary_t maybe_word_boundary(string str, int pos) {
    int len = str.length();
    if (pos < 0 || pos >= len) {
        return (boundary_t){.type = E_BOUNDARY_TYPE_ERROR};
    } else {
        if (pos == 0 && is_word_char(str[pos])) {
            // if the first character is word-y, we have a left boundary at the beginning
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos};
        } else if (pos == len - 1 && is_word_char(str[pos])) {
            // if the last character is word-y, we have a right boundary left of the null terminator
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        } else if (!is_word_char(str[pos]) && is_word_char(str[pos + 1])) {
            // if we have a delimiter followed by a word char, we have a left boundary left of the word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos + 1};
        } else if (is_word_char(str[pos]) && !is_word_char(str[pos + 1])) {
            // if we have a word char followed by a delimiter, we have a right boundary right of the word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        }
        return (boundary_t){.type = E_BOUNDARY_TYPE_NONE};
    }
}

int main() {
    string str;
    getline(cin, str);

    int len = str.length();
    for (int i = 0; i < len; i++) {
        boundary_t boundary = maybe_word_boundary(str, i);
        if (boundary.type == E_BOUNDARY_TYPE_LEFT) {
            // whatever
        } else if (boundary.type == E_BOUNDARY_TYPE_RIGHT) {
            // whatever
        }
    }
}

Как видите, код очень прост для понимания и тонкой настройки, а фактическое использование кода очень короткое и простое. Использование C++ не должно останавливать нас от написания простейшего и наиболее легко настраиваемого кода, даже если это означает отказ от STL. Я бы подумал, что это пример того, что Линус Торвальдс мог бы назвать "вкус", поскольку мы устранили всю логику, которая нам не нужна, при написании в стиле, который, естественно, позволяет обрабатывать больше дел, когда и если возникает необходимость в их обработке.

Что могло бы улучшить этот код, так это использование enum class, принятие указателя функции на is_word_char в maybe_word_boundary вместо прямого вызова is_word_char и передача лямбда.

Версия C++ 17 без выделения памяти (кроме std::function)

void iter_words(const std::string_view& input, const std::function<void(std::string_view)>& process_word) {

    auto itr = input.begin();

    auto consume_whitespace = [&]() {
        for(; itr != input.end(); ++itr) {
            if (!isspace(*itr))
                return;
        }
    };

    auto consume_letters = [&]() {
        for(; itr != input.end(); ++itr) {
            if (isspace(*itr))
                return;
        }
    };

    while(true) {
        consume_whitespace();
        if (itr == input.end())
            return;
        auto word_start = itr - input.begin();
        consume_letters();
        auto word_end = itr - input.begin();
        process_word(input.substr(word_start, word_end - word_start));
    }
}

int main() {
    iter_words("foo bar", [](std::string_view sv) {
        std::cout << "Got word: " <<  sv << '\n';
    });
    return 0;
}

C++ 20 наконец-то благословил нас функцией split. А точнее переходник диапазона. Godbolt ссылка.

#include <iostream>
#include <ranges>
#include <string_view>

namespace ranges = std::ranges;
namespace views = std::views;

using str = std::string_view;

constexpr auto view =
    "Multiple words"
    | views::split(' ')
    | views::transform([](auto &&r) -> str {
        return {
            &*r.begin(),
            static_cast<str::size_type>(ranges::distance(r))
        };
    });

auto main() -> int {
    for (str &&sv : view) {
        std::cout << sv << '\n';
    }
}

Это выглядит НАМНОГО сложнее, чем первоначально предложенное решение. Вам не нужно проделывать столько работы, чтобы разбить строку!

UserX 10.11.2020 10:17

Все ответили за предопределенный строковый ввод. Я думаю, что этот ответ поможет кому-то для отсканированного ввода.

Я использовал вектор токенов для хранения токенов строк. Это необязательно.

#include <bits/stdc++.h>

using namespace std ;
int main()
{
    string str, token ;
    getline(cin, str) ; // get the string as input
    istringstream ss(str); // insert the string into tokenizer

    vector<string> tokens; // vector tokens holds the tokens

    while (ss >> token) tokens.push_back(token); // splits the tokens
    for(auto x : tokens) cout << x << endl ; // prints the tokens

    return 0;
}


образец ввода:

port city international university

образец вывода:

port
city
international
university

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

char delimiter = ',' ;
while(getline(ss, token, delimiter)) tokens.push_back(token) ;

вместо

while (ss >> token) tokens.push_back(token);

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