Я не могу поверить, что эта рутинная задача - такая головная боль в c ++
wfbarksdale
6
Это не головная боль в C ++ - есть разные способы добиться этого. программисты менее осведомлены о c ++, чем c # - речь
идет
9
@ hB0, проходящий через множество вопросов, ответов и все еще не решающий, является головной болью. одна нуждается в этой библиотеке, другая только для пробелов, другая не обрабатывает пробелы ..
Алгоритмы стандартной библиотеки C ++ довольно универсально основаны на итераторах, а не на конкретных контейнерах. К сожалению, это затрудняет предоставление Java-подобной splitфункции в стандартной библиотеке C ++, хотя никто не утверждает, что это было бы удобно. Но каким будет тип возвращаемого значения? std::vector<std::basic_string<…>>? Возможно, но тогда мы вынуждены выполнять (потенциально избыточные и дорогостоящие) распределения.
Вместо этого C ++ предлагает множество способов разделения строк на основе произвольно сложных разделителей, но ни один из них не инкапсулирован так хорошо, как в других языках. Многочисленные способы заполнить целые сообщения в блоге .
В простейшем случае вы можете использовать итерацию, std::string::findпока не нажмете std::string::npos, и извлечь содержимое с помощью std::string::substr.
Более гибкая (и идиоматическая, но базовая) версия для разделения на пробельные символы будет использовать std::istringstream:
auto iss = std::istringstream{"The quick brown fox"};auto str = std::string{};while(iss >> str){
process(str);}
Используя std::istream_iterators , содержимое строкового потока также может быть скопировано в вектор, используя его конструктор диапазона итератора.
Несколько библиотек (например, Boost.Tokenizer ) предлагают определенные токенизаторы.
Более сложное разбиение требует регулярных выражений. C ++ предоставляет std::regex_token_iteratorдля этой цели, в частности:
autoconst str ="The quick brown fox"s;autoconst re = std::regex{R"(\s+)"};autoconst vec = std::vector<std::string>(
std::sregex_token_iterator{begin(str), end(str), re,-1},
std::sregex_token_iterator{});
К сожалению, повышение не всегда доступно для всех проектов. Я должен искать ответ без поддержки.
FuzzyBunnySlippers
36
Не каждый проект открыт для «открытого источника». Я работаю в строго регулируемых отраслях. Это не проблема, правда. Это просто факт жизни. Повышение доступно не везде.
FuzzyBunnySlippers
5
@NonlinearIdeas Другой вопрос / ответ вообще не касался проектов с открытым исходным кодом. То же самое верно для любого проекта. Тем не менее, я, конечно, понимаю об ограниченных стандартах, таких как MISRA C, но потом понятно, что вы все равно строите все с нуля (если только вы не найдете подходящую библиотеку - редкость). В любом случае, дело не в том, что «Повышение недоступно» - это то, что у вас есть особые требования, к которым почти любой ответ общего назначения был бы неподходящим.
Конрад Рудольф
1
@NonlinearIdeas Показательный пример, другие, не-Boost ответы также не совместимы с MISRA.
Конрад Рудольф
3
@Dmitry Что такое "STL barf" ?! И все сообщество очень поддерживает замену препроцессора C - на самом деле, есть предложения сделать это. Но ваше предложение использовать PHP или другой язык вместо этого было бы огромным шагом назад.
Конрад Рудольф
188
Класс Boost Tokenizer может сделать такие вещи довольно простыми:
#include<iostream>#include<string>#include<boost/foreach.hpp>#include<boost/tokenizer.hpp>usingnamespace std;usingnamespace boost;int main(int,char**){
string text ="token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char>> tokens(text, sep);
BOOST_FOREACH (const string& t, tokens){
cout << t <<"."<< endl;}}
Обновлено для C ++ 11:
#include<iostream>#include<string>#include<boost/tokenizer.hpp>usingnamespace std;usingnamespace boost;int main(int,char**){
string text ="token, test string";
char_separator<char> sep(", ");
tokenizer<char_separator<char>> tokens(text, sep);for(constauto& t : tokens){
cout << t <<"."<< endl;}}
Хорошие вещи, я недавно использовал это. Мой компилятор Visual Studio имеет странный оттенок до тех пор, пока я не использую пробел для разделения двух символов «>» перед битом токенов (text, sep): (ошибка C2947: ожидается «>» для завершения шаблона-аргумента-списка, найдено '> > ')
AndyUK
@AndyUK да, без пробела компилятор анализирует его как оператор извлечения, а не два закрывающих шаблона.
EnabrenTane
Теоретически это было исправлено в C ++ 0x
Дэвид Саутер
3
Остерегайтесь третьих параметров char_separatorконструктора ( drop_empty_tokensпо умолчанию, альтернатива есть keep_empty_tokens).
Бенуа
5
@puk - это часто используемый суффикс для заголовочных файлов C ++. (как .hдля заголовков C)
Ферруччо
167
Вот очень простой пример:
#include<vector>#include<string>usingnamespace std;vector<string> split(constchar*str,char c =' '){vector<string> result;do{constchar*begin = str;while(*str != c &&*str)
str++;
result.push_back(string(begin, str));}while(0!=*str++);return result;}
мне нужно добавить прототип для этого метода в файле .h?
Сухроб Самиев
5
Это не совсем «лучший» ответ, так как он все еще использует строковый литерал, который представляет собой простой символьный массив констант Си. Я полагаю, что спрашивающий спрашивал, может ли он токенизировать строку C ++, которая имеет тип "string", введенный последним.
Виджай Кумар Канта
Это требует нового ответа, потому что я сильно подозреваю, что включение регулярных выражений в C ++ 11 изменило лучший ответ.
всевозможный
114
Используйте стрток. На мой взгляд, нет необходимости создавать класс вокруг токенизации, если только strtok не предоставит вам то, что вам нужно. Возможно, нет, но за 15 с лишним лет написания различного кода синтаксического анализа на C и C ++ я всегда использовал strtok. Вот пример
char myString[]="The quick brown fox";char*p = strtok(myString," ");while(p){
printf ("Token: %s\n", p);
p = strtok(NULL," ");}
Несколько предостережений (которые могут не соответствовать вашим потребностям). В процессе «строка» «уничтожается», что означает, что символы EOS помещаются в строку в точках разделителя. Для правильного использования может потребоваться сделать неконстантную версию строки. Вы также можете изменить список разделителей mid parse.
По моему мнению, приведенный выше код гораздо проще и проще в использовании, чем написание для него отдельного класса. Для меня это одна из тех функций, которые предоставляет язык, и делает это хорошо и чисто. Это просто решение на основе Си. Это уместно, это легко, и вам не нужно писать много лишнего кода :-)
Не то, чтобы мне не нравился C, однако strtok не является поточно-ориентированным, и вам нужно быть уверенным, что отправляемая строка содержит нулевой символ, чтобы избежать возможного переполнения буфера.
разговор
11
Есть strtok_r, но это был вопрос C ++.
Профессор Фалькен нарушил контракт
3
@tloach: в MS C ++ компилятор strtok является поточно-ориентированным, поскольку внутренняя статическая переменная создается в TLS (локальное хранилище потоков) (фактически это зависит от компилятора)
Ахмед Саид
3
@ahmed: потокобезопасность означает больше, чем просто возможность дважды запустить функцию в разных потоках. В этом случае, если поток изменяется во время работы strtok, возможно, что строка будет действительной в течение всего запуска strtok, но strtok все равно будет портиться из-за того, что строка изменилась, она уже прошла нулевой символ и собирается продолжайте читать память, пока она не получит нарушение безопасности или не найдет нулевой символ. Это проблема с исходными строковыми функциями C, если вы не указали длину где-то, с чем сталкиваетесь.
tloach
4
Для strtok требуется указатель на неконстантный массив символов с нулевым символом в конце, который не часто встречается в коде c ++ ... Какой ваш любимый способ преобразования в него из std :: string?
fuzzyTew
106
Еще один быстрый способ заключается в использовании getline. Что-то вроде:
У меня были проблемы с использованием этой техники с символами 0x0A в строке, что привело к преждевременному завершению цикла while. В противном случае это хорошее простое и быстрое решение.
Райан Х.
4
Это хорошо, но нужно помнить, что при этом разделитель по умолчанию '\ n' не учитывается. Этот пример будет работать, но если вы используете что-то вроде: while (getline (inFile, word, '')) где inFile - это объект ifstream, содержащий несколько строк, вы получите смешные результаты ..
hackrock
Жаль, что getline возвращает поток, а не строку, что делает его непригодным для использования в списках инициализации без временного хранения
fuzzyTew
1
Круто! Никакого повышения и C ++ 11, хорошее решение для этих устаревших проектов!
Deqing
1
Это ответ, название функции немного неловко.
Нильс
82
Вы можете использовать потоки, итераторы и алгоритм копирования, чтобы сделать это довольно напрямую.
#include<string>#include<vector>#include<iostream>#include<istream>#include<ostream>#include<iterator>#include<sstream>#include<algorithm>int main(){
std::string str ="The quick brown fox";// construct a stream from the string
std::stringstream strstr(str);// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);}
Я нахожу эти std :: раздражающими, чтобы читать .. почему бы не использовать "использование"?
user35978
80
@Vadi: потому что редактирование чужого поста довольно навязчиво. @pheze: Я предпочитаю, чтобы stdтаким образом я знал, откуда мой объект, это просто вопрос стиля.
Матье М.
7
Я понимаю вашу причину и думаю, что это действительно хороший выбор, если он работает для вас, но с педагогической точки зрения я на самом деле согласен с Фезе. Легче читать и понимать совершенно чужой пример, такой как этот, с «использованием пространства имен std» вверху, потому что он требует меньше усилий для интерпретации следующих строк ... особенно в этом случае, потому что все из стандартной библиотеки. Вы можете сделать его легко читаемым и очевидным, откуда берутся объекты, с помощью серии "using std :: string;" и т.д. Тем более что функция такая короткая.
Чеширеков
61
Несмотря на то, что префиксы «std ::» раздражают или уродливы, лучше включить их в пример кода, чтобы было совершенно ясно, откуда берутся эти функции. Если они вас беспокоят, заменить их на «использование» тривиально после того, как вы украдете пример и заявите, что он ваш.
dlchambers
20
Ага! что он сказал! Передовой опыт - использовать префикс std. Любая большая кодовая база, без сомнения, будет иметь свои собственные библиотеки и пространства имен, а использование «using namespace std» доставит вам головную боль, когда вы начнете вызывать конфликты пространства имен.
Miek
48
Не обижайтесь , ребята, но для такой простой задачи, вы делаете вещи путь слишком сложен. Есть много причин использовать Boost . Но для чего-то такого простого, это все равно что летать на санях с 20 #.
void
split(vector<string>& theStringVector,/* Altered/returned value */const string & theString,const string & theDelimiter){
UASSERT( theDelimiter.size(),>,0);// My own ASSERT macro.size_t start =0, end =0;while( end != string::npos){
end = theString.find( theDelimiter, start);// If at end, use length=maxLength. Else use length=end-start.
theStringVector.push_back( theString.substr( start,(end == string::npos)? string::npos : end - start));// If at end, use start=maxSize. Else use start=end+delimiter.
start =(( end >(string::npos - theDelimiter.size()))? string::npos : end + theDelimiter.size());}}
Например (для Дуга)
#define SHOW(I,X) cout <<"["<<(I)<<"]\t "# X " = \"" << (X) << "\"" << endlint
main(){vector<string> v;
split( v,"A:PEP:909:Inventory Item",":");for(unsignedint i =0; i < v.size(); i++)
SHOW( i, v[i]);}
И да, мы могли бы использовать метод split (), возвращающий новый вектор, а не передавая его. Это тривиально для переноса и перегрузки. Но в зависимости от того, что я делаю, я часто нахожу лучше повторно использовать уже существующие объекты, чем всегда создавать новые. (Пока я не забуду опустошить вектор между ними!)
Зачем определять макрос, который вы используете только в одном месте. И как ваш UASSERT лучше, чем стандартное утверждение? Разделение сравнения на 3 токена, как это, ничего не делает, кроме того, что требуется больше запятых, чем нужно в противном случае.
Крелбор
1
Может быть, макрос UASSERT показывает (в сообщении об ошибке) фактическую связь между (и значениями) двух сравниваемых значений? На самом деле это очень хорошая идея, ИМХО.
GhassanPL
10
Тьфу, почему std::stringкласс не включает функцию split ()?
Мистер Шикаданс
Я думаю, что последняя строка в цикле while должна быть, start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());а во время цикла должна быть while (start != string::npos). Кроме того, я проверяю подстроку, чтобы убедиться, что она не пуста, прежде чем вставлять ее в вектор.
Джон К,
@JohnK Если вход имеет два последовательных разделителя, то ясно, что строка между ними пуста и должна быть вставлена в вектор. Если пустые значения неприемлемы для конкретной цели, это другое дело, но ИМХО такие ограничения должны применяться вне этого вида функций общего назначения.
Лаури Нурми
46
Решение с использованием regex_token_iterators:
#include<iostream>#include<regex>#include<string>usingnamespace std;int main(){
string str("The quick brown fox");
regex reg("\\s+");
sregex_token_iterator iter(str.begin(), str.end(), reg,-1);
sregex_token_iterator end;vector<string> vec(iter, end);for(auto a : vec){
cout << a << endl;}}
Это должен быть самый лучший ответ. Это правильный способ сделать это в C ++> = 11.
всевозможный
1
Я рад, что прокрутил весь ответ до этого ответа (в настоящее время было только 9 голосов). Именно так должен выглядеть код C ++ 11 для этой задачи!
YePhIcK
Отличный ответ, который не зависит от внешних библиотек и использует уже имеющиеся библиотеки
Эндрю
1
Отличный ответ, дающий наибольшую гибкость в разделителях. Несколько предостережений: использование \ s + regex позволяет избежать пустых токенов в середине текста, но дает пустой первый токен, если текст начинается с пробела. Кроме того, регулярное выражение кажется медленным: на моем ноутбуке для 20 МБ случайного текста это занимает 0,6 с, по сравнению с 0,014 с для ответа strtok, strsep или Parham с использованием str.find_first_of, или 0,027 с для Perl, или 0,021 с для Python , Для короткого текста скорость не может быть проблемой.
Марк Гейтс
2
Хорошо, возможно, это выглядит круто, но это явно чрезмерное использование регулярных выражений. Разумно, только если вы не заботитесь о производительности.
#include<vector>#include<boost/algorithm/string.hpp>int main(){auto s ="a,b, c ,,e,f,";
std::vector<std::string> fields;
boost::split(fields, s, boost::is_any_of(","));for(constauto& field : fields)
std::cout <<"\""<< field <<"\"\n";return0;}
Это простое STL-решение (~ 5 строк!), Использующее std::findи std::find_first_not_ofобрабатывающее повторения разделителя (например, пробелы или точки), а также ведущие и конечные разделители:
#include<string>#include<vector>void tokenize(std::string str, std::vector<string>&token_v){size_t start = str.find_first_not_of(DELIMITER), end=start;while(start != std::string::npos){// Find next occurence of delimiter
end = str.find(DELIMITER, start);// Push back the token found into vector
token_v.push_back(str.substr(start, end-start));// Skip all occurences of the delimiter to find new start
start = str.find_first_not_of(DELIMITER, end);}}
Это хороший вариант, но я думаю, что вам нужно использовать find_first_of () вместо find () для правильной работы с несколькими разделителями.
2
@ user755921 пропускаются несколько разделителей при поиске начальной позиции с помощью find_first_not_of.
Новичок
16
pystring - это небольшая библиотека, которая реализует набор строковых функций Python, включая метод split:
#include<string>#include<vector>#include"pystring.h"
std::vector<std::string> chunks;
pystring::split("this string", chunks);// also can specify a separator
pystring::split("this-string", chunks,"-");
Вау, вы ответили на мой ближайший вопрос и многие будущие вопросы. Я понимаю, что C ++ является мощным. Но когда расщепление строки приводит к исходному коду, подобному приведенным выше ответам, это явно обескураживает. Я хотел бы знать о других библиотеках, таких как эта, которые снижают удобство языков.
Росс
вау, ты серьезно только что сделал мой день !! не знал о пистринге. это сэкономит мне много времени!
accraze
11
Я разместил этот ответ на аналогичный вопрос.
Не изобретай велосипед. Я использовал несколько библиотек, и самая быстрая и гибкая из всех, с которыми я сталкивался, это: C ++ String Toolkit Library .
Вот пример того, как его использовать, который я разместил где-то в стеке потока.
#include<iostream>#include<vector>#include<string>#include<strtk.hpp>constchar*whitespace =" \t\r\n\f";constchar*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;}}return0;}
#include<iostream>#include<sstream>usingnamespace std;int main (){
string tmps;
istringstream is ("the dellimiter is the space");while(is.good ()){
is >> tmps;
cout << tmps <<"\n";}return0;}
Эта функция Tokenize () пропускает пустые токены, например, если в основной строке есть подстрока "%%", пустой возвращаемый токен не возвращается. Это пропущено.
Sheen
4
Если вы хотите использовать C, вы можете использовать функцию strtok . Вы должны обратить внимание на проблемы многопоточности при его использовании.
Обратите внимание, что strtok изменяет строку, которую вы проверяете, поэтому вы не можете использовать ее в строках const char * без создания копии.
Грэм Перроу
9
Проблема многопоточности заключается в том, что strtok использует глобальную переменную, чтобы отслеживать, где она находится, поэтому, если у вас есть два потока, каждый из которых использует strtok, вы получите неопределенное поведение.
JohnMcG
@JohnMcG Или просто использовать, strtok_sчто в основном strtokс явной передачей состояния.
Трусливый отказ от ответственности: я пишу программное обеспечение для обработки данных в режиме реального времени, где данные поступают через двоичные файлы, сокеты или некоторые вызовы API (карты ввода-вывода, камеры). Я никогда не использую эту функцию для чего-то более сложного или критичного ко времени, чем чтение внешних файлов конфигурации при запуске.
+1 за предложение регулярных выражений, если вам не нужна скорость деформации, это самое гибкое решение, которое пока не поддерживается везде, но со временем это станет менее важным.
Одиннадцатое
+1 от меня, только что попробовал <regex> в c ++ 11. Так просто и элегантно
StahlRat
4
Здесь много слишком сложных предложений. Попробуйте это простое решение std :: string:
Я виноват в том, что дал плохой (слишком простой) пример. Насколько я знаю, это работает только тогда, когда ваш разделитель является пробелом.
Билл Ящерица
4
В ответе Адама Пирса приведен токенайзер, вращающийся вручную const char*. С итераторами это немного более проблематично, потому что увеличение stringконечного итератора не определено . Тем не менее, учитывая, что string str{ "The quick brown fox" }мы, безусловно, можем достичь этого:
Если вы хотите абстрагироваться от сложности с помощью стандартной функциональности, как предлагает On Freund,strtok это простой вариант:
vector<string> tokens;for(auto i = strtok(data(str)," "); i !=nullptr; i = strtok(nullptr," ")) tokens.push_back(i);
Если у вас нет доступа к C ++ 17, вам нужно заменить, data(str)как в этом примере: http://ideone.com/8kAGoa
Хотя это не продемонстрировано в примере, strtokне обязательно использовать один и тот же разделитель для каждого токена. Вместе с этим преимуществом есть несколько недостатков:
strtokнельзя использовать для нескольких stringsодновременно: nullptrнеобходимо либо передать a для продолжения токенизации текущего, stringлибо char*должен быть передан новый токенизации (однако есть несколько нестандартных реализаций, которые поддерживают это, например:strtok_s )
По той же причине strtokнельзя использовать несколько потоков одновременно (однако это может быть определено реализацией, например: реализация Visual Studio является поточно-ориентированной )
Вызов strtokизменяет тот метод, над которым stringон работает, поэтому его нельзя использовать в const strings, const char*s или литеральных строках, чтобы токенизировать любой из них с помощью strtokили работать с stringсодержимым who, который необходимо сохранить, strего необходимо скопировать, тогда копия может оперироваться
Предыдущие методы не могут генерировать токены vectorна месте, т. Е. Не абстрагировать их в вспомогательную функцию, которую они не могут инициализировать const vector<string> tokens. Эта функциональность и возможность принимать любой разделитель пробелов можно использовать с помощью istream_iterator. Например, учитывая: const string str{ "The quick \tbrown \nfox" }мы можем сделать это:
Требуемая конструкция istringstreamдля этого варианта имеет гораздо большую стоимость, чем предыдущие 2 варианта, однако эта стоимость обычно скрывается за счет stringраспределения.
Если ни один из вышеперечисленных вариантов не является достаточно гибким для удовлетворения ваших потребностей в токенизации, наиболее гибким вариантом, regex_token_iteratorконечно, является использование этой гибкости, которое сопряжено с большими затратами, но, опять же, это, вероятно, скрыто в stringстоимости выделения. Скажем, например, что мы хотим токенизировать на основе неэкранированных запятых, также используя пробел, с учетом следующего ввода: const string str{ "The ,qu\\,ick ,\tbrown, fox" }мы можем сделать это:
strtok_sэто стандарт C11, кстати. strtok_rэто стандарт POSIX2001. Между ними есть стандартная версия strtokдля большинства платформ.
Андон М. Колман
@ AndonM.Coleman Но это вопрос c ++ , а в C ++ #include <cstring>включает только версию c99strtok . Итак, я предполагаю, что вы просто предоставляете этот комментарий как вспомогательный материал, демонстрирующий доступность strtokрасширений для конкретной реализации ?
Джонатан Ми
1
Просто это не так нестандартно, как люди могли бы поверить. strtok_sпредоставляется как C11, так и в качестве отдельного расширения во время выполнения Microsoft C. Здесь есть любопытная история, когда _sфункции Microsoft стали стандартом C.
Андон М. Коулман
@ AndonM.Coleman Хорошо, я с тобой. Очевидно, что если это стандарт C11, интерфейс и реализация имеют ограничения, которые требуют идентичного поведения независимо от платформы. Теперь единственной проблемой является обеспечение того, чтобы функция C11 была доступна для нас на разных платформах. Надеемся, что стандарт C11 будет тем, что C ++ 17 или C ++ 20 выберет для пикапа.
Джонатан Ми
3
Я знаю, что на этот вопрос уже дан ответ, но я хочу внести свой вклад. Может быть, мое решение немного простое, но вот что я придумал:
Мне кажется странным, что со всеми нами, любителями скорости, здесь, на SO, никто не представил версию, которая использует сгенерированную во время компиляции справочную таблицу для разделителя (пример реализации ниже). Использование справочной таблицы и итераторов должно превзойти эффективность std :: regex, если вам не нужно разбивать регулярное выражение, просто используйте его, его стандарт на C ++ 11 и супер гибкий.
Некоторые уже предложили регулярное выражение, но для noobs вот упакованный пример, который должен делать именно то, что ожидает OP:
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};while(std::regex_search (it,end,m,e)){
ret.emplace_back(m.str());
std::advance(it, m.position()+ m.length());//next start position = match position + match length}return ret;}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){//comfort version calls flexible versionreturn split(s.cbegin(), s.cend(), std::move(e));}int main (){
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};auto v = split(str);for(constauto&s:v){
std::cout << s << std::endl;}
std::cout <<"crazy version:"<< std::endl;
v = split(str, std::regex{"[^e]+"});//using e as delim shows flexibilityfor(constauto&s:v){
std::cout << s << std::endl;}return0;}
Если нам нужно быть быстрее и принять ограничение, что все символы должны быть 8 битами, мы можем создать таблицу поиска во время компиляции, используя метапрограммирование:
template<bool...>structBoolSequence{};//just here to hold boolstemplate<char...>structCharSequence{};//just here to hold charstemplate<typename T,char C>structContains;//generictemplate<charFirst,char...Cs,charMatch>//not first specializationstructContains<CharSequence<First,Cs...>,Match>:Contains<CharSequence<Cs...>,Match>{};//strip first and increase indextemplate<charFirst,char...Cs>//is first specializationstructContains<CharSequence<First,Cs...>,First>: std::true_type {};template<charMatch>//not found specializationstructContains<CharSequence<>,Match>: std::false_type{};template<int I,typename T,typename U>structMakeSequence;//generictemplate<int I,bool...Bs,typename U>structMakeSequence<I,BoolSequence<Bs...>, U>://not lastMakeSequence<I-1,BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};template<bool...Bs,typename U>structMakeSequence<0,BoolSequence<Bs...>,U>{//last usingType=BoolSequence<Bs...>;};template<typename T>structBoolASCIITable;template<bool...Bs>structBoolASCIITable<BoolSequence<Bs...>>{/* could be made constexpr but not yet supported by MSVC */staticbool isDelim(constchar c){staticconstbool table[256]={Bs...};return table[static_cast<int>(c)];}};usingDelims=CharSequence<'.',',',' ',':','\n'>;//list your custom delimiters hereusingTable=BoolASCIITable<typenameMakeSequence<256,BoolSequence<>,Delims>::Type>;
Благодаря этому сделать getNextTokenфункцию легко:
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{}));//find first non delim or endauto second = std::find_if(begin,end,Table{});//find first delim or endreturn std::make_pair(begin,second);}
Используя это также легко:
int main(){
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};auto it = std::begin(s);auto end = std::end(s);while(it != std::end(s)){auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second)<< std::endl;
it = token.second;}return0;}
эта версия оптимизирована только для разделителей из одного символа, использование таблицы поиска не подходит для разделителей из нескольких символов (строк), поэтому ее сложнее превзойти по регулярному выражению.
Одиннадцатое
1
Вы можете воспользоваться boost :: make_find_iterator. Что-то похожее на это:
template<typename CH>inlinevector< basic_string<CH>> tokenize(const basic_string<CH>&Input,const basic_string<CH>&Delimiter,bool remove_empty_token
){typedeftypename basic_string<CH>::const_iteratorstring_iterator_t;typedef boost::find_iterator<string_iterator_t>string_find_iterator_t;vector< basic_string<CH>>Result;string_iterator_t it =Input.begin();string_iterator_t it_end =Input.end();for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i !=string_find_iterator_t();++i){if(remove_empty_token){if(it != i->begin())Result.push_back(basic_string<CH>(it,i->begin()));}elseResult.push_back(basic_string<CH>(it,i->begin()));
it = i->end();}if(it != it_end)Result.push_back(basic_string<CH>(it,it_end));returnResult;}
Вот мой Swiss® Army Knife струнных токенизаторов для разделения строк по пробелам, учета строк в одинарных и двойных кавычках, а также удаления этих символов из результатов. Я использовал RegexBuddy 4.x для генерации большей части фрагмента кода, но я добавил пользовательскую обработку для удаления кавычек и некоторые другие вещи.
(Низкие) голоса могут быть такими же конструктивными, как и голоса против, но не тогда, когда вы не оставляете комментарии о том, почему ...
kayleeFrye_onDeck
1
Я выровнял вас, но это может быть потому, что код выглядит довольно пугающим для программиста, который ищет в Google «как разбить строку», особенно без документации
mattshu
Спасибо @mattshu! Это сегменты регулярных выражений, которые делают его пугающим или что-то еще?
kayleeFrye_onDeck
0
Если максимальная длина входной строки, подлежащей токенизации, известна, можно воспользоваться этим и реализовать очень быструю версию. Ниже я делаю набросок основной идеи, которая была вдохновлена как strtok (), так и структурой данных «массив суффиксов», описанной «Программированием Perls» Джона Бентли, 2-е издание, глава 15. Класс C ++ в этом случае дает лишь некоторую организацию и удобство. использования. Показанная реализация может быть легко расширена для удаления начальных и конечных пробельных символов в токенах.
По сути, можно заменить символы-разделители символами '\ 0', заканчивающимися на строку, и установить указатели на токены с измененной строкой. В крайнем случае, когда строка состоит только из разделителей, каждый получает длину строки плюс 1, что приводит к пустым токенам. Практично дублировать строку, которую нужно изменить.
Заголовочный файл:
classTextLineSplitter{public:TextLineSplitter(constsize_t max_line_len );~TextLineSplitter();voidSplitLine(constchar*line,constchar sep_char =',',);inlinesize_tNumTokens(void)const{return mNumTokens;}constchar*GetToken(constsize_t token_idx )const{
assert( token_idx < mNumTokens );return mTokens[ token_idx ];}private:constsize_t mStorageSize;char*mBuff;char**mTokens;size_t mNumTokens;inlinevoidResetContent(void){
memset( mBuff,0, mStorageSize );// mark all items as empty:
memset( mTokens,0, mStorageSize *sizeof(char*));// reset counter for found items:
mNumTokens =0L;}};
// create an instance capable of splitting strings up to 1000 chars long:TextLineSplitter spl(1000);
spl.SplitLine("Item1,,Item2,Item3");for(size_t i =0; i < spl.NumTokens(); i++){
printf("%s\n", spl.GetToken( i ));}
boost::tokenizerВаш друг, но подумайте над тем, чтобы сделать ваш код переносимым со ссылкой на проблемы интернационализации (i18n), используя wstring/ wchar_tвместо устаревших string/ charтипов.
#include<iostream>#include<boost/tokenizer.hpp>#include<string>usingnamespace std;usingnamespace boost;typedef tokenizer<char_separator<wchar_t>,
wstring::const_iterator, wstring>Tok;int main(){
wstring s;while(getline(wcin, s)){
char_separator<wchar_t> sep(L" ");// list of separator charactersTok tok(s, sep);for(Tok::iterator beg = tok.begin(); beg != tok.end();++beg){
wcout <<*beg << L"\t";// output (or store in vector)}
wcout << L"\n";}return0;}
«Наследие» определенно не является правильным и wchar_tявляется ужасным типом, зависящим от реализации, который никто не должен использовать, если это не является абсолютно необходимым.
CoffeeandCode
Использование wchar_t как-то не решает автоматически проблемы i18n. Вы используете кодировки для решения этой проблемы. Если вы разделяете строку разделителем, подразумевается, что разделитель не конфликтует с закодированным содержимым любого токена внутри строки. Может потребоваться побег и т. Д. Wchar_t не является волшебным решением для этого.
Йонил
0
Простой код C ++ (стандарт C ++ 98), принимает несколько разделителей (указанных в std :: string), использует только векторы, строки и итераторы.
#include<iostream>#include<vector>#include<string>#include<stdexcept>
std::vector<std::string>
split(const std::string& str,const std::string& delim){
std::vector<std::string> result;if(str.empty())throw std::runtime_error("Can not tokenize an empty string!");
std::string::const_iterator begin, str_it;
begin = str_it = str.begin();do{while(delim.find(*str_it)== std::string::npos && str_it != str.end())
str_it++;// find the position of the first delimiter in str
std::string token = std::string(begin, str_it);// grab the tokenif(!token.empty())// empty token only when str starts with a delimiter
result.push_back(token);// push the token into a vector<string>while(delim.find(*str_it)!= std::string::npos && str_it != str.end())
str_it++;// ignore the additional consecutive delimiters
begin = str_it;// process the remaining tokens}while(str_it != str.end());return result;}int main(){
std::string test_string =".this is.a.../.simple;;test;;;END";
std::string delim ="; ./";// string containing the delimiters
std::vector<std::string> tokens = split(test_string, delim);for(std::vector<std::string>::const_iterator it = tokens.begin();
it != tokens.end(); it++)
std::cout <<*it << std::endl;}
Ответы:
Алгоритмы стандартной библиотеки C ++ довольно универсально основаны на итераторах, а не на конкретных контейнерах. К сожалению, это затрудняет предоставление Java-подобной
split
функции в стандартной библиотеке C ++, хотя никто не утверждает, что это было бы удобно. Но каким будет тип возвращаемого значения?std::vector<std::basic_string<…>>
? Возможно, но тогда мы вынуждены выполнять (потенциально избыточные и дорогостоящие) распределения.Вместо этого C ++ предлагает множество способов разделения строк на основе произвольно сложных разделителей, но ни один из них не инкапсулирован так хорошо, как в других языках. Многочисленные способы заполнить целые сообщения в блоге .
В простейшем случае вы можете использовать итерацию,
std::string::find
пока не нажметеstd::string::npos
, и извлечь содержимое с помощьюstd::string::substr
.Более гибкая (и идиоматическая, но базовая) версия для разделения на пробельные символы будет использовать
std::istringstream
:Используя
std::istream_iterator
s , содержимое строкового потока также может быть скопировано в вектор, используя его конструктор диапазона итератора.Несколько библиотек (например, Boost.Tokenizer ) предлагают определенные токенизаторы.
Более сложное разбиение требует регулярных выражений. C ++ предоставляет
std::regex_token_iterator
для этой цели, в частности:источник
Класс Boost Tokenizer может сделать такие вещи довольно простыми:
Обновлено для C ++ 11:
источник
char_separator
конструктора (drop_empty_tokens
по умолчанию, альтернатива естьkeep_empty_tokens
)..h
для заголовков C)Вот очень простой пример:
источник
Используйте стрток. На мой взгляд, нет необходимости создавать класс вокруг токенизации, если только strtok не предоставит вам то, что вам нужно. Возможно, нет, но за 15 с лишним лет написания различного кода синтаксического анализа на C и C ++ я всегда использовал strtok. Вот пример
Несколько предостережений (которые могут не соответствовать вашим потребностям). В процессе «строка» «уничтожается», что означает, что символы EOS помещаются в строку в точках разделителя. Для правильного использования может потребоваться сделать неконстантную версию строки. Вы также можете изменить список разделителей mid parse.
По моему мнению, приведенный выше код гораздо проще и проще в использовании, чем написание для него отдельного класса. Для меня это одна из тех функций, которые предоставляет язык, и делает это хорошо и чисто. Это просто решение на основе Си. Это уместно, это легко, и вам не нужно писать много лишнего кода :-)
источник
Еще один быстрый способ заключается в использовании
getline
. Что-то вроде:Если вы хотите, вы можете сделать простой
split()
метод, возвращающий avector<string>
, который действительно полезен.источник
Вы можете использовать потоки, итераторы и алгоритм копирования, чтобы сделать это довольно напрямую.
источник
std
таким образом я знал, откуда мой объект, это просто вопрос стиля.Не обижайтесь , ребята, но для такой простой задачи, вы делаете вещи путь слишком сложен. Есть много причин использовать Boost . Но для чего-то такого простого, это все равно что летать на санях с 20 #.
Например (для Дуга)
И да, мы могли бы использовать метод split (), возвращающий новый вектор, а не передавая его. Это тривиально для переноса и перегрузки. Но в зависимости от того, что я делаю, я часто нахожу лучше повторно использовать уже существующие объекты, чем всегда создавать новые. (Пока я не забуду опустошить вектор между ними!)
Ссылка: http://www.cplusplus.com/reference/string/string/ .
(Первоначально я писал ответ на вопрос Дуга: « Изменение и извлечение строк C ++ на основе разделителей» (закрыто) . Но поскольку Мартин Йорк закрыл этот вопрос указателем, я просто обобщу свой код.)
источник
std::string
класс не включает функцию split ()?start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());
а во время цикла должна бытьwhile (start != string::npos)
. Кроме того, я проверяю подстроку, чтобы убедиться, что она не пуста, прежде чем вставлять ее в вектор.Решение с использованием
regex_token_iterator
s:источник
Boost имеет сильную функцию разбиения: boost ::gorith :: split .
Пример программы:
Вывод:
источник
Я знаю, что вы спрашивали о решении C ++, но вы можете посчитать это полезным:
Qt
Преимущество над Boost в этом примере состоит в том, что это прямое сопоставление кода вашего сообщения.
Смотрите больше в документации Qt
источник
Вот пример класса токенизатора, который может делать то, что вы хотите
Пример:
источник
Это простое STL-решение (~ 5 строк!), Использующее
std::find
иstd::find_first_not_of
обрабатывающее повторения разделителя (например, пробелы или точки), а также ведущие и конечные разделители:Попробуйте это вживую !
источник
pystring - это небольшая библиотека, которая реализует набор строковых функций Python, включая метод split:
источник
Я разместил этот ответ на аналогичный вопрос.
Не изобретай велосипед. Я использовал несколько библиотек, и самая быстрая и гибкая из всех, с которыми я сталкивался, это: C ++ String Toolkit Library .
Вот пример того, как его использовать, который я разместил где-то в стеке потока.
источник
Проверьте этот пример. Это может помочь вам ..
источник
while ( is >> tmps ) { std::cout << tmps << "\n"; }
MFC / ATL имеет очень хороший токенизатор. Из MSDN:
источник
Если вы хотите использовать C, вы можете использовать функцию strtok . Вы должны обратить внимание на проблемы многопоточности при его использовании.
источник
strtok_s
что в основномstrtok
с явной передачей состояния.Для простых вещей я просто использую следующее:
Трусливый отказ от ответственности: я пишу программное обеспечение для обработки данных в режиме реального времени, где данные поступают через двоичные файлы, сокеты или некоторые вызовы API (карты ввода-вывода, камеры). Я никогда не использую эту функцию для чего-то более сложного или критичного ко времени, чем чтение внешних файлов конфигурации при запуске.
источник
Вы можете просто использовать библиотеку регулярных выражений и решить эту проблему с помощью регулярных выражений.
Используйте выражение (\ w +) и переменную в \ 1 (или $ 1 в зависимости от реализации библиотеки регулярных выражений).
источник
Здесь много слишком сложных предложений. Попробуйте это простое решение std :: string:
источник
Я подумал, что это то, для чего предназначен
>>
оператор на строковых потоках:источник
В ответе Адама Пирса приведен токенайзер, вращающийся вручную
const char*
. С итераторами это немного более проблематично, потому что увеличениеstring
конечного итератора не определено . Тем не менее, учитывая, чтоstring str{ "The quick brown fox" }
мы, безусловно, можем достичь этого:Live Example
Если вы хотите абстрагироваться от сложности с помощью стандартной функциональности, как предлагает On Freund,
strtok
это простой вариант:Если у вас нет доступа к C ++ 17, вам нужно заменить,
data(str)
как в этом примере: http://ideone.com/8kAGoaХотя это не продемонстрировано в примере,
strtok
не обязательно использовать один и тот же разделитель для каждого токена. Вместе с этим преимуществом есть несколько недостатков:strtok
нельзя использовать для несколькихstrings
одновременно:nullptr
необходимо либо передать a для продолжения токенизации текущего,string
либоchar*
должен быть передан новый токенизации (однако есть несколько нестандартных реализаций, которые поддерживают это, например:strtok_s
)strtok
нельзя использовать несколько потоков одновременно (однако это может быть определено реализацией, например: реализация Visual Studio является поточно-ориентированной )strtok
изменяет тот метод, над которымstring
он работает, поэтому его нельзя использовать вconst string
s,const char*
s или литеральных строках, чтобы токенизировать любой из них с помощьюstrtok
или работать сstring
содержимым who, который необходимо сохранить,str
его необходимо скопировать, тогда копия может оперироватьсяC ++ 20предоставляет нам
split_view
токенизацию строк неразрушающим способом: https://topanswers.xyz/cplusplus?q=749#a874Предыдущие методы не могут генерировать токены
vector
на месте, т. Е. Не абстрагировать их в вспомогательную функцию, которую они не могут инициализироватьconst vector<string> tokens
. Эта функциональность и возможность принимать любой разделитель пробелов можно использовать с помощьюistream_iterator
. Например, учитывая:const string str{ "The quick \tbrown \nfox" }
мы можем сделать это:Live Example
Требуемая конструкция
istringstream
для этого варианта имеет гораздо большую стоимость, чем предыдущие 2 варианта, однако эта стоимость обычно скрывается за счетstring
распределения.Если ни один из вышеперечисленных вариантов не является достаточно гибким для удовлетворения ваших потребностей в токенизации, наиболее гибким вариантом,
regex_token_iterator
конечно, является использование этой гибкости, которое сопряжено с большими затратами, но, опять же, это, вероятно, скрыто вstring
стоимости выделения. Скажем, например, что мы хотим токенизировать на основе неэкранированных запятых, также используя пробел, с учетом следующего ввода:const string str{ "The ,qu\\,ick ,\tbrown, fox" }
мы можем сделать это:Live Example
источник
strtok_s
это стандарт C11, кстати.strtok_r
это стандарт POSIX2001. Между ними есть стандартная версияstrtok
для большинства платформ.#include <cstring>
включает только версию c99strtok
. Итак, я предполагаю, что вы просто предоставляете этот комментарий как вспомогательный материал, демонстрирующий доступностьstrtok
расширений для конкретной реализации ?strtok_s
предоставляется как C11, так и в качестве отдельного расширения во время выполнения Microsoft C. Здесь есть любопытная история, когда_s
функции Microsoft стали стандартом C.Я знаю, что на этот вопрос уже дан ответ, но я хочу внести свой вклад. Может быть, мое решение немного простое, но вот что я придумал:
Пожалуйста, прокомментируйте, есть ли лучший подход к чему-либо в моем коде или если что-то не так.
ОБНОВЛЕНИЕ: добавлен общий разделитель
источник
Вот подход, который позволяет вам контролировать, включены ли пустые токены (например, strsep) или исключены (например, strtok).
источник
Мне кажется странным, что со всеми нами, любителями скорости, здесь, на SO, никто не представил версию, которая использует сгенерированную во время компиляции справочную таблицу для разделителя (пример реализации ниже). Использование справочной таблицы и итераторов должно превзойти эффективность std :: regex, если вам не нужно разбивать регулярное выражение, просто используйте его, его стандарт на C ++ 11 и супер гибкий.
Некоторые уже предложили регулярное выражение, но для noobs вот упакованный пример, который должен делать именно то, что ожидает OP:
Если нам нужно быть быстрее и принять ограничение, что все символы должны быть 8 битами, мы можем создать таблицу поиска во время компиляции, используя метапрограммирование:
Благодаря этому сделать
getNextToken
функцию легко:Используя это также легко:
Вот живой пример: http://ideone.com/GKtkLQ
источник
Вы можете воспользоваться boost :: make_find_iterator. Что-то похожее на это:
источник
Вот мой Swiss® Army Knife струнных токенизаторов для разделения строк по пробелам, учета строк в одинарных и двойных кавычках, а также удаления этих символов из результатов. Я использовал RegexBuddy 4.x для генерации большей части фрагмента кода, но я добавил пользовательскую обработку для удаления кавычек и некоторые другие вещи.
источник
Если максимальная длина входной строки, подлежащей токенизации, известна, можно воспользоваться этим и реализовать очень быструю версию. Ниже я делаю набросок основной идеи, которая была вдохновлена как strtok (), так и структурой данных «массив суффиксов», описанной «Программированием Perls» Джона Бентли, 2-е издание, глава 15. Класс C ++ в этом случае дает лишь некоторую организацию и удобство. использования. Показанная реализация может быть легко расширена для удаления начальных и конечных пробельных символов в токенах.
По сути, можно заменить символы-разделители символами '\ 0', заканчивающимися на строку, и установить указатели на токены с измененной строкой. В крайнем случае, когда строка состоит только из разделителей, каждый получает длину строки плюс 1, что приводит к пустым токенам. Практично дублировать строку, которую нужно изменить.
Заголовочный файл:
Файл реализации:
Сценарий использования будет:
вывод:
источник
boost::tokenizer
Ваш друг, но подумайте над тем, чтобы сделать ваш код переносимым со ссылкой на проблемы интернационализации (i18n), используяwstring
/wchar_t
вместо устаревшихstring
/char
типов.источник
wchar_t
является ужасным типом, зависящим от реализации, который никто не должен использовать, если это не является абсолютно необходимым.Простой код C ++ (стандарт C ++ 98), принимает несколько разделителей (указанных в std :: string), использует только векторы, строки и итераторы.
источник