Как сделать токенизацию строки в C ++?
Java имеет удобный метод разделения:
String str = "The quick brown fox"; String[] results = str.split(" ");
Есть ли простой способ сделать это на C ++?
- Есть ли функция для разделения строки в PL / SQL?
- Как использовать stringstream для разделения разделенных запятыми строк
- Каков самый простой / лучший / самый правильный способ перебора символов строки в Java?
- Сканер против StringTokenizer против String.Split
- Как читать ввод по-символам в Java?
- Как получить токен из Lucene TokenStream?
- Тонирование и сортировка с помощью XSLT 1.0
- Ищете четкое определение того, что такое «токенизатор», «парсер» и «лексеры» и как они связаны друг с другом и используются?
- Шаблоны C ++ Угловые скобки Pitfall - Что такое C ++ 11?
- Токенизация строк в C
- Разделение разделенной запятой строки в PL / SQL хранимой процедуре
- Ошибка Tokenizing: java.util.regex.PatternSyntaxException, оборванный метасимвол '*'
- Вложенная функция функции strtok в C
Ваш простой случай можно легко построить с помощью метода std::string::find
. Однако взгляните на Boost.Tokenizer . Здорово. У Boost обычно есть очень крутые струнные инструменты.
Класс токенизатора Boost может сделать это довольно простым:
#include #include #include #include using namespace std; using namespace boost; int main(int, char**) { string text = "token, test string"; char_separator sep(", "); tokenizer< char_separator > tokens(text, sep); BOOST_FOREACH (const string& t, tokens) { cout << t << "." << endl; } }
Обновлено для C ++ 11:
#include #include #include using namespace std; using namespace boost; int main(int, char**) { string text = "token, test string"; char_separator sep(", "); tokenizer> tokens(text, sep); for (const auto& t : tokens) { cout << t << "." << endl; } }
Вот простой:
#include #include using namespace std; vector split(const char *str, char c = ' ') { vector result; do { const char *begin = str; while(*str != c && *str) str++; result.push_back(string(begin, str)); } while (0 != *str++); return result; }
Используйте strtok. На мой взгляд, нет необходимости создавать class вокруг токенизации, если 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 помещаются в строковые точки. Правильное использование может потребовать от вас создать неконстантную версию строки. Вы также можете изменить список разграничений синтаксиса разделителей.
По моему мнению, приведенный выше код намного проще и проще в использовании, чем писать для него отдельный class. Для меня это одна из тех функций, которые предоставляет язык, и делает это хорошо и чисто. Это просто решение на основе C. Это уместно, это легко, и вам не нужно писать много дополнительного кода 🙂
Еще один быстрый способ – использовать getline
. Что-то вроде:
stringstream ss("bla bla"); string s; while (getline(ss, s, ' ')) { cout << s << endl; }
Если вы хотите, вы можете сделать простой метод split()
возвращающий vector
, что действительно полезно.
Вы можете использовать streamи, iteratorы и алгоритм копирования, чтобы сделать это достаточно прямо.
#include #include #include #include #include #include #include #include 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 it(strstr); std::istream_iterator end; std::vector results(it, end); // send the vector to stdout. std::ostream_iterator oit(std::cout); std::copy(results.begin(), results.end(), oit); }
Не обижайтесь, но для такой простой проблемы вы делаете вещи слишком сложными. Существует множество причин использовать Boost . Но для чего-то такого простого, это похоже на удар мухой с 20 санями.
void split( vector & 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) << "\"" << endl int main() { vector v; split( v, "A:PEP:909:Inventory Item", ":" ); for (unsigned int i = 0; i < v.size(); i++) SHOW( i, v[i] ); }
И да, мы могли бы split () вернуть новый вектор, а не передавать его. Тривиально обернуть и перегрузить. Но в зависимости от того, что я делаю, я часто считаю, что лучше использовать ранее существовавшие объекты, а не создавать новые. (Пока я не забываю опорожнить вектор между ними!)
Ссылка: http://www.cplusplus.com/reference/string/string/ .
(Я изначально писал ответ на вопрос Дуга: C ++ Strings Modifying and Extracting на основе Separators (закрыто) . Но так как Martin York закрыл этот вопрос с помощью указателя здесь ... я просто обобщу свой код.)
Boost имеет сильную функцию разделения: boost :: algorithm :: split .
Пример программы:
#include #include int main() { auto s = "a,b, c ,,e,f,"; std::vector fields; boost::split(fields, s, boost::is_any_of(",")); for (const auto& field : fields) std::cout << "\"" << field << "\"\n"; return 0; }
Вывод:
"a" "b" " c " "" "e" "f" ""
Решение с использованием regex_token_iterator
s:
#include #include #include using namespace 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 vec(iter, end); for (auto a : vec) { cout << a << endl; } }
Я знаю, что вы попросили решение на C ++, но вы можете подумать об этом:
Qt
#include ... QString str = "The quick brown fox"; QStringList results = str.split(" ");
Преимущество над Boost в этом примере состоит в том, что это прямое сопоставление с кодом вашего сообщения.
Подробнее о документации Qt
Вот пример classа токенизатора, который может делать то, что вы хотите
//Header file class Tokenizer { public: static const std::string DELIMITERS; Tokenizer(const std::string& str); Tokenizer(const std::string& str, const std::string& delimiters); bool NextToken(); bool NextToken(const std::string& delimiters); const std::string GetToken() const; void Reset(); protected: size_t m_offset; const std::string m_string; std::string m_token; std::string m_delimiters; }; //CPP file const std::string Tokenizer::DELIMITERS(" \t\n\r"); Tokenizer::Tokenizer(const std::string& s) : m_string(s), m_offset(0), m_delimiters(DELIMITERS) {} Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) : m_string(s), m_offset(0), m_delimiters(delimiters) {} bool Tokenizer::NextToken() { return NextToken(m_delimiters); } bool Tokenizer::NextToken(const std::string& delimiters) { size_t i = m_string.find_first_not_of(delimiters, m_offset); if (std::string::npos == i) { m_offset = m_string.length(); return false; } size_t j = m_string.find_first_of(delimiters, i); if (std::string::npos == j) { m_token = m_string.substr(i); m_offset = m_string.length(); return true; } m_token = m_string.substr(i, j - i); m_offset = j; return true; }
Пример:
std::vector v; Tokenizer s("split this string", " "); while (s.NextToken()) { v.push_back(s.GetToken()); }
Это простое решение STL (~ 5 строк!), Использующее std::find
и std::find_first_not_of
который обрабатывает повторы разделителя (например, пробелы или периоды), а также ведущие и конечные разделители:
#include #include void tokenize(std::string str, std::vector &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); } }
Попробуйте прямо сейчас!
pystring – небольшая библиотека, которая реализует кучу строковых функций Python, включая метод split:
#include #include #include "pystring.h" std::vector chunks; pystring::split("this string", chunks); // also can specify a separator pystring::split("this-string", chunks, "-");
Я отправил этот ответ на аналогичный вопрос.
Не изобретайте велосипед. Я использовал несколько библиотек, и самый быстрый и самый гибкий, с которым я столкнулся, – это: C ++ String Toolkit Library .
Вот пример того, как использовать его, который я разместил еще где-то в stackoverflow.
#include #include #include #include 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 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 t("3.0, 3.14; 4.0"); std::vector 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 u("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; }
Проверьте этот пример. Это может помочь вам.
#include #include using namespace std; int main () { string tmps; istringstream is ("the dellimiter is the space"); while (is.good ()) { is >> tmps; cout << tmps << "\n"; } return 0; }
Вы можете просто использовать библиотеку регулярных выражений и решить эту проблему с помощью регулярных выражений.
Используйте выражение (\ w +) и переменную в \ 1 (или $ 1 в зависимости от реализации регулярных выражений библиотеки).
Если вы хотите использовать C, вы можете использовать функцию strtok . При использовании его следует обратить внимание на multithreading.
Для простых вещей я просто использую следующее:
unsigned TokenizeString(const std::string& i_source, const std::string& i_seperators, bool i_discard_empty_tokens, std::vector& o_tokens) { unsigned prev_pos = 0; unsigned pos = 0; unsigned number_of_tokens = 0; o_tokens.clear(); pos = i_source.find_first_of(i_seperators, pos); while (pos != std::string::npos) { std::string token = i_source.substr(prev_pos, pos - prev_pos); if (!i_discard_empty_tokens || token != "") { o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos)); number_of_tokens++; } pos++; prev_pos = pos; pos = i_source.find_first_of(i_seperators, pos); } if (prev_pos < i_source.length()) { o_tokens.push_back(i_source.substr(prev_pos)); number_of_tokens++; } return number_of_tokens; }
Трудный отказ от ответственности: я пишу программное обеспечение для обработки данных в реальном времени, где данные поступают через двоичные файлы, сокеты или какой-либо вызов API (карты ввода-вывода, камеры). Я никогда не использую эту функцию для чего-то более сложного или критического по времени, чем чтение внешних файлов конфигурации при запуске.
MFC / ATL имеет очень хороший токенизатор. Из MSDN:
CAtlString str( "%First Second#Third" ); CAtlString resToken; int curPos= 0; resToken= str.Tokenize("% #",curPos); while (resToken != "") { printf("Resulting token: %s\n", resToken); resToken= str.Tokenize("% #",curPos); }; Output Resulting Token: First Resulting Token: Second Resulting Token: Third
Здесь слишком много сложных предложений. Попробуйте это простое решение std :: string:
using namespace std; string someText = ... string::size_type tokenOff = 0, sepOff = tokenOff; while (sepOff != string::npos) { sepOff = someText.find(' ', sepOff); string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff; string token = someText.substr(tokenOff, tokenLen); if (!token.empty()) /* do something with token */; tokenOff = sepOff; }
Я думал, что это был оператор >>
для streamовых streamов:
string word; sin >> word;
Ответ Адама Пирса дает рутинный токенизатор, принимающий const char*
. Это немного более проблематично в отношении iteratorов, потому что инкремент конечного iteratorа string
не определен . Тем не менее, учитывая string str{ "The quick brown fox" }
мы можем это сделать:
auto start = find(cbegin(str), cend(str), ' '); vector tokens{ string(cbegin(str), start) }; while (start != cend(str)) { const auto finish = find(++start, cend(str), ' '); tokens.push_back(string(start, finish)); start = finish; }
Живой пример
Если вы ищете абстрактную сложность, используя стандартную функциональность, так как On Freund предлагает strtok
– это простой вариант:
vector 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, чтобы продолжить токенирование текущейstring
либо должен быть передан новыйchar*
tokenize (есть некоторые нестандартные реализации, которые поддерживают это, однако, например, :strtok_s
) - По той же причине
strtok
нельзя использовать одновременно для нескольких streamов (это может быть, однако, реализовано, например: реализация Visual Studio является streamобезопасной ) - Вызов
strtok
изменяетstring
он работает, поэтому он не может использоваться вconst string
s,const char*
s или literal string, чтобы токенизировать любой из них с помощьюstrtok
или работать сstring
, содержимое которой необходимо сохранить,str
необходимо скопировать, затем копировать можно было бы
Оба предыдущих метода не могут генерировать токенированный vector
на месте, то есть без абстрагирования их в вспомогательную функцию они не могут инициализировать const vector
. Эта функциональность и возможность принимать любой разделитель белого пробела можно использовать с помощью istream_iterator
. Например: const string str{ "The quick \tbrown \nfox" }
мы можем сделать это:
istringstream is{ str }; const vector tokens{ istream_iterator (is), istream_iterator () };
Живой пример
Требуемое построение istringstream
для этой опции имеет гораздо большую стоимость, чем предыдущие 2 варианта, однако эта стоимость обычно скрыта за счет распределения string
.
Если ни один из вышеперечисленных вариантов достаточно гибкий для ваших потребностей в токенизации, наиболее гибким вариантом является использование regex_token_iterator
конечно, с такой гибкостью regex_token_iterator
большие затраты, но опять же это, вероятно, скрыто в стоимости распределения string
. Скажем, например, мы хотим tokenize на основе неэкранированных запятых, также использующих белое пространство, учитывая следующий вход: const string str{ "The ,qu\\,ick ,\tbrown, fox" }
мы можем это сделать:
const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" }; const vector tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };
Живой пример
Вот подход, который позволяет вам контролировать, включены ли пустые токены (например, strsep) или исключены (например, strtok).
#include // for strchr and strlen /* * want_empty_tokens==true : include empty tokens, like strsep() * want_empty_tokens==false : exclude empty tokens, like strtok() */ std::vector tokenize(const char* src, char delim, bool want_empty_tokens) { std::vector tokens; if (src and *src != '\0') // defensive while( true ) { const char* d = strchr(src, delim); size_t len = (d)? d-src : strlen(src); if (len or want_empty_tokens) tokens.push_back( std::string(src, len) ); // capture token if (d) src += len+1; else break; } return tokens; }
Seems odd to me that with all us speed conscious nerds here on SO no one has presented a version that uses a compile time generated look up table for the delimiter (example implementation further down). Using a look up table and iterators should beat std::regex in efficiency, if you don’t need to beat regex, just use it, its standard as of C++11 and super flexible.
Some have suggested regex already but for the noobs here is a packaged example that should do exactly what the OP expects:
std::vector split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){ std::smatch m{}; std::vector 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 split(const std::string &s, std::regex e = std::regex{"\\w+"}){ //comfort version calls flexible version return 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(const auto&s:v){ std::cout << s << std::endl; } std::cout << "crazy version:" << std::endl; v = split(str, std::regex{"[^e]+"}); //using e as delim shows flexibility for(const auto&s:v){ std::cout << s << std::endl; } return 0; }
If we need to be faster and accept the constraint that all chars must be 8 bits we can make a look up table at compile time using metaprogramming:
template struct BoolSequence{}; //just here to hold bools template struct CharSequence{}; //just here to hold chars template struct Contains; //generic template //not first specialization struct Contains,Match> : Contains, Match>{}; //strip first and increase index template //is first specialization struct Contains,First>: std::true_type {}; template //not found specialization struct Contains,Match>: std::false_type{}; template struct MakeSequence; //generic template struct MakeSequence, U>: //not last MakeSequence::value,Bs...>, U>{}; template struct MakeSequence<0,BoolSequence,U>{ //last using Type = BoolSequence; }; template struct BoolASCIITable; template struct BoolASCIITable>{ /* could be made constexpr but not yet supported by MSVC */ static bool isDelim(const char c){ static const bool table[256] = {Bs...}; return table[static_cast(c)]; } }; using Delims = CharSequence<'.',',',' ',':','\n'>; //list your custom delimiters here using Table = BoolASCIITable,Delims>::Type>;
With that in place making a getNextToken
function is easy:
template std::pair getNextToken(T_It begin,T_It end){ begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end auto second = std::find_if(begin,end,Table{}); //find first delim or end return std::make_pair(begin,second); }
Using it is also easy:
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; } return 0; }
Here is a live example: http://ideone.com/GKtkLQ
There is no direct way to do this. Refer this code project source code to find out how to build a class for this.
you can take advantage of boost::make_find_iterator. Something similar to this:
template inline vector< basic_string > tokenize( const basic_string &Input, const basic_string &Delimiter, bool remove_empty_token ) { typedef typename basic_string ::const_iterator string_iterator_t; typedef boost::find_iterator< string_iterator_t > string_find_iterator_t; vector< basic_string > 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 (it,i->begin())); } else Result.push_back(basic_string (it,i->begin())); it = i->end(); } if(it != it_end) Result.push_back(basic_string (it,it_end)); return Result; }
If the maximum length of the input string to be tokenized is known, one can exploit this and implement a very fast version. I am sketching the basic idea below, which was inspired by both strtok() and the “suffix array”-data structure described Jon Bentley’s “Programming Perls” 2nd edition, chapter 15. The C++ class in this case only gives some organization and convenience of use. The implementation shown can be easily extended for removing leading and trailing whitespace characters in the tokens.
Basically one can replace the separator characters with string-terminating ‘\0’-characters and set pointers to the tokens withing the modified string. In the extreme case when the string consists only of separators, one gets string-length plus 1 resulting empty tokens. It is practical to duplicate the string to be modified.
Заголовок файла:
class TextLineSplitter { public: TextLineSplitter( const size_t max_line_len ); ~TextLineSplitter(); void SplitLine( const char *line, const char sep_char = ',', ); inline size_t NumTokens( void ) const { return mNumTokens; } const char * GetToken( const size_t token_idx ) const { assert( token_idx < mNumTokens ); return mTokens[ token_idx ]; } private: const size_t mStorageSize; char *mBuff; char **mTokens; size_t mNumTokens; inline void ResetContent( void ) { memset( mBuff, 0, mStorageSize ); // mark all items as empty: memset( mTokens, 0, mStorageSize * sizeof( char* ) ); // reset counter for found items: mNumTokens = 0L; } };
Implementattion file:
TextLineSplitter::TextLineSplitter( const size_t max_line_len ): mStorageSize ( max_line_len + 1L ) { // allocate memory mBuff = new char [ mStorageSize ]; mTokens = new char* [ mStorageSize ]; ResetContent(); } TextLineSplitter::~TextLineSplitter() { delete [] mBuff; delete [] mTokens; } void TextLineSplitter::SplitLine( const char *line, const char sep_char /* = ',' */, ) { assert( sep_char != '\0' ); ResetContent(); strncpy( mBuff, line, mMaxLineLen ); size_t idx = 0L; // running index for characters do { assert( idx < mStorageSize ); const char chr = line[ idx ]; // retrieve current character if( mTokens[ mNumTokens ] == NULL ) { mTokens[ mNumTokens ] = &mBuff[ idx ]; } // if if( chr == sep_char || chr == '\0' ) { // item or line finished // overwrite separator with a 0-terminating character: mBuff[ idx ] = '\0'; // count-up items: mNumTokens ++; } // if } while( line[ idx++ ] ); }
A scenario of usage would be:
// 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 ) ); }
вывод:
Item1 Item2 Item3
boost::tokenizer
is your friend, but consider making your code portable with reference to internationalization (i18n) issues by using wstring
/ wchar_t
instead of the legacy string
/ char
types.
#include #include #include using namespace std; using namespace boost; typedef tokenizer, wstring::const_iterator, wstring> Tok; int main() { wstring s; while (getline(wcin, s)) { char_separator sep(L" "); // list of separator characters Tok 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"; } return 0; }
Simple C++ code (standard C++98), accepts multiple delimiters (specified in a std::string), uses only vectors, strings and iterators.
#include #include #include #include std::vector split(const std::string& str, const std::string& delim){ std::vector 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 token if (!token.empty()) // empty token only when str starts with a delimiter result.push_back(token); // push the token into a vector 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 tokens = split(test_string, delim); for (std::vector ::const_iterator it = tokens.begin(); it != tokens.end(); it++) std::cout << *it << std::endl; }
/// split a string into multiple sub strings, based on a separator string /// for example, if separator="::", /// /// s = "abc" -> "abc" /// /// s = "abc::def xy::st:" -> "abc", "def xy" and "st:", /// /// s = "::abc::" -> "abc" /// /// s = "::" -> NO sub strings found /// /// s = "" -> NO sub strings found /// /// then append the sub-strings to the end of the vector v. /// /// the idea comes from the findUrls() function of "Accelerated C++", chapt7, /// findurls.cpp /// void split(const string& s, const string& sep, vector& v) { typedef string::const_iterator iter; iter b = s.begin(), e = s.end(), i; iter sep_b = sep.begin(), sep_e = sep.end(); // search through s while (b != e){ i = search(b, e, sep_b, sep_e); // no more separator found if (i == e){ // it's not an empty string if (b != e) v.push_back(string(b, e)); break; } else if (i == b){ // the separator is found and right at the beginning // in this case, we need to move on and search for the // next separator b = i + sep.length(); } else{ // found the separator v.push_back(string(b, i)); b = i; } } }
The boost library is good, but they are not always available. Doing this sort of things by hand is also a good brain exercise. Here we just use the std::search() algorithm from the STL, see the above code.