2008-09-10 10 views

ответ

129

Ваш простой случай может быть легко построен с использованием метода std::string::find. Однако взгляните на Boost.Tokenizer. Здорово. У Boost обычно есть очень крутые струнные инструменты.

22

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

//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 <std::string> v; 
Tokenizer s("split this string", " "); 
while (s.NextToken()) 
{ 
    v.push_back(s.GetToken()); 
} 
4

Если вы хотите использовать C, вы можете использовать функцию strtok. При использовании его следует обратить внимание на многопоточность.

+3

Обратите внимание, что strtok модифицирует строку, которую вы проверяете, поэтому вы не можете использовать ее в строках const char * без копирования. – 2008-09-10 13:53:20

+7

Проблема многопоточности заключается в том, что strtok использует глобальную переменную, чтобы отслеживать, где она находится, поэтому, если у вас есть два потока, каждый из которых использует strtok, вы получите неопределенное поведение. – JohnMcG 2008-09-10 15:09:34

151

Вот реальный простой:

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

vector<string> split(const char *str, char c = ' ') 
{ 
    vector<string> result; 

    do 
    { 
     const char *begin = str; 

     while(*str != c && *str) 
      str++; 

     result.push_back(string(begin, str)); 
    } while (0 != *str++); 

    return result; 
} 
4

Я думал, что это то, что >> оператор строковых потоков был для:

string word; sin >> word; 
+1

Моя вина за плохой (слишком простой) пример. Насколько я знаю, это работает только тогда, когда ваш разделитель является пробелом. – 2008-11-25 18:24:35

79

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

#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); 
} 
+16

Я нахожу эти std :: раздражающие, чтобы читать .. почему бы не использовать «использование»? – user35978 2008-11-28 04:19:27

+2

@pheze: сэр, почему бы вам не отредактировать, а не жаловаться? – asyncwait 2009-10-27 14:28:04

+74

@ Вади: потому что редактирование чужого сообщения довольно навязчиво. @pheze: Я предпочитаю, чтобы `std` таким образом я знаю, откуда мой объект, это просто вопрос стиля. – 2010-04-02 08:49:00

100

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

Несколько предостережений (которые могут не соответствовать вашим потребностям). Строка «уничтожена» в процессе, что означает, что символы EOS помещаются в строковые точки. Правильное использование может потребовать от вас создать неконстантную версию строки. Вы также можете изменить список разграничений синтаксиса разделителей.

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

+35

Не то, что мне не нравится C, однако strtok не является потокобезопасным, и вам нужно быть уверенным, что строка, которую вы отправляете, содержит нулевой символ, чтобы избежать возможного переполнения буфера – tloach 2010-05-10 13:18:33

+8

Существует strtok_r, но это был вопрос C++. – 2010-10-06 09:14:03

+3

@tloach: в компиляторе MS C++ strtok является потокобезопасным, поскольку внутренняя статическая переменная создается на TLS (локальное хранилище потоков) (на самом деле это зависит от компилятора) – 2010-11-28 15:03:30

179

Boost tokenizer класса может сделать такого рода вещи довольно просто:

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

using namespace std; 
using namespace 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> 

using namespace std; 
using namespace boost; 

int main(int, char**) 
{ 
    string text = "token, test string"; 

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

Хороший материал, я недавно использовал это. Мой компилятор Visual Studio имеет странное представление, пока я не использую пробел для разделения двух символов «>» перед битами tokens (text, sep): (ошибка C2947: ожидается, что «>» завершает шаблон-аргумент-список, найденный> > ') – AndyUK 2010-10-01 15:57:54

+0

@ AndyUK да, без пробела компилятор анализирует его как оператор извлечения, а не два закрывающих шаблона. – EnabrenTane 2011-06-14 03:23:14

+0

Теоретически это было исправлено в C++ 0x – 2011-09-01 02:09:27

34

Boost имеет сильную функцию разделения: boost::algorithm::split.

Пример программы:

#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 (const auto& field : fields) 
     std::cout << "\"" << field << "\"\n"; 
    return 0; 
} 

Выход:

"a" 
"b" 
" c " 
"" 
"e" 
"f" 
"" 
4

Для простых вещей, которые я просто использовать следующее:

unsigned TokenizeString(const std::string& i_source, 
         const std::string& i_seperators, 
         bool i_discard_empty_tokens, 
         std::vector<std::string>& 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 (карты ввода/вывода, камеры). Я никогда не использую эту функцию для чего-то более сложного или критического по времени, чем чтение внешних файлов конфигурации при запуске.

46

Не обижайтесь, ребята, но для такой простой задачи, вы делаете вещи путь слишком сложно. Существует множество причин использовать 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) << "\"" << endl 

int 
main() 
{ 
    vector<string> v; 

    split(v, "A:PEP:909:Inventory Item", ":"); 

    for (unsigned int i = 0; i < v.size(); i++) 
     SHOW(i, v[i]); 
} 

И да, мы могли бы раскол() возвращает новый вектор, а не проходя один в. Это тривиально, чтобы обернуть и перегрузки. Но в зависимости от того, что я делаю, я часто считаю, что лучше использовать ранее существовавшие объекты, а не создавать новые. (До тех пор, пока я не забываю опорожнить вектор между ними!)

Справка: http://www.cplusplus.com/reference/string/string/.

(я был первоначально писать ответ на вопрос: Дуга. C++ Strings Modifying and Extracting based on Separators (closed) Но так как Мартин Йорк закрыл этот вопрос с указателем здесь ... Я просто обобщать свой код.)

+11

Зачем определять макрос, который вы используете только в одном месте. И как ваш UASSERT лучше, чем стандартное утверждение. Разделение сравнения на 3 жетона вроде ничего не требует больше запятых, чем вам в противном случае. – crelbor 2011-05-13 13:10:04

87

Другой быстрый способ заключается в использование getline. Что-то вроде:

stringstream ss("bla bla"); 
string s; 

while (getline(ss, s, ' ')) { 
cout << s << endl; 
} 

Если вы хотите, вы можете сделать простой split() метод возвращающего vector<string>, который действительно полезно.

+2

У меня были проблемы с использованием этой техники с символами 0x0A в строке, из-за чего выход цикла был преждевременным. В противном случае это простое и быстрое решение. – 2011-01-24 23:00:48

4

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 
+0

Эта функция Tokenize() пропускает пустые токены, например, если в основной строке есть подстрока «%%», то пустой возвращаемый токен не возвращается. Он пропущен. – Sheen 2011-01-20 16:49:40

23

Я знаю, что вы просили решение C++, но вы могли бы рассмотреть это полезно:

Qt

#include <QString> 

... 

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 

Преимущество по сравнению с Boost, в этом примере, что это прямое сопоставление с кодом вашего сообщения.

Смотри на Qt documentation

8

Проверьте этот пример. Это может помочь вам ..

#include <iostream> 
#include <sstream> 

using namespace std; 

int main() 
{ 
    string tmps; 
    istringstream is ("the dellimiter is the space"); 
    while (is.good()) { 
     is >> tmps; 
     cout << tmps << "\n"; 
    } 
    return 0; 
} 
5

Вы можете просто использовать regular expression library и решить, что с помощью регулярных выражений.

Использовать выражение (\ w +) и переменную в \ 1 (или $ 1 в зависимости от реализации регулярных выражений библиотеки).

0

Если известна максимальная длина входной строки, подлежащей токенизации, ее можно использовать и реализовать очень быструю версию. Я набросал основную идею ниже, которая была вдохновлена ​​как strtok(), так и структурой «суффикс-массив», описанной в главе «Программирование Perls» Джона Бентли 2-го издания, глава 15. Класс C++ в этом случае дает только некоторую организацию и удобство использования. Показанная реализация может быть легко расширена для удаления символов и пробелов в токенах.

В принципе, символы разделителя можно заменить символами '\ 0'-символов, заканчивающимися строкой, и установить указатели на токены с измененной строкой. В крайнем случае, когда строка состоит только из разделителей, получается строка длиной плюс 1 результирующие пустые маркеры. Практически дублировать строку, подлежащую изменению.

Заголовочный файл:

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 файл:

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++ ]); 
} 

Сценарий использования будет:

// 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 
1

вы можете воспользоваться boost :: make_find_iterator. Что-то похожее на это:

template<typename CH> 
inline vector< basic_string<CH> > tokenize(
    const basic_string<CH> &Input, 
    const basic_string<CH> &Delimiter, 
    bool remove_empty_token 
    ) { 

    typedef typename basic_string<CH>::const_iterator string_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())); 
     } 
     else 
      Result.push_back(basic_string<CH>(it,i->begin())); 
     it = i->end(); 
    } 
    if(it != it_end) 
     Result.push_back(basic_string<CH>(it,it_end)); 

    return Result; 
} 
16

pystring это небольшая библиотека, которая реализует кучу строковых функций Python, в том числе методом расщепленного:

#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, "-"); 
0

boost::tokenizer является вашим другом, но считаю что делает ваш код переносимым с ссылки на вопросы интернационализации (i18n) с использованием wstring/wchar_t вместо устаревших string/char типов.

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

using namespace std; 
using namespace 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 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; 
} 
4

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

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; 
} 
2

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

#include <string.h> // for strchr and strlen 

/* 
* want_empty_tokens==true : include empty tokens, like strsep() 
* want_empty_tokens==false : exclude empty tokens, like strtok() 
*/ 
std::vector<std::string> tokenize(const char* src, 
            char delim, 
            bool want_empty_tokens) 
{ 
    std::vector<std::string> 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; 
} 
-3

Это простая петля для tokenise с только стандартной библиотеки файлов

#include <iostream.h> 
#include <stdio.h> 
#include <string.h> 
#include <math.h> 
#include <conio.h> 
class word 
    { 
    public: 
    char w[20]; 
    word() 
     { 
     for(int j=0;j<=20;j++) 
     {w[j]='\0'; 
     } 
    } 



}; 

void main() 
    { 
    int i=1,n=0,j=0,k=0,m=1; 
    char input[100]; 
    word ww[100]; 
    gets(input); 

    n=strlen(input); 


    for(i=0;i<=m;i++) 
     { 
     if(context[i]!=' ') 
     { 
      ww[k].w[j]=context[i]; 
      j++; 

     } 
     else 
     { 
     k++; 
     j=0; 
     m++; 
     } 

    } 
} 
0

Простой C++ код (стандарт C++ 98), принимает несколько разделителей (указанные в станд :: строка), использует только векторы, строки и итераторы.

#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 token 
     if (!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; 
} 
9

Я отправил этот ответ на аналогичный вопрос.
Не изобретайте велосипед.Я использовал несколько библиотек, и самый быстрый и самый гибкий, с которым я столкнулся, - это: C++ String Toolkit Library.

Вот пример того, как использовать его, который я разместил еще там, где в стеке.

#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 
     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 

     string t("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 

     string u("angle = 45; radius = 9.9"); 
     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; 
} 
0
/// 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<string>& 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; 
     } 
    } 
} 

Библиотека подталкивание хорошо, но они не всегда доступны. Выполнение такого рода вещей вручную также является хорошим упражнением на мозг. Здесь мы просто используем алгоритм std :: search() из STL, см. Приведенный выше код.

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; 
} 

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

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

Надеюсь, это поможет.

2

Мне кажется странным, что со всеми нами уверенными ботаниками здесь, на SO, никто не представил версию, которая использует таблицу времени сгенерированного времени для разделителя (пример реализации дальше вниз). Использование таблицы поиска и итераторов должно эффективно бить std :: regex, если вам не нужно бить регулярное выражение, просто используйте его, его стандарт как C++ 11 и супер гибкий.

Некоторые из них предложил регулярное выражение уже, но для недоносков здесь является упакована примером, который должен делать именно то, что ожидает, что 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 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; 
} 

Если нам нужно быть быстрее и принять ограничение, что все символы должны быть 8 биты мы можем сделать справочную таблицу во время компиляции с помощью метапрограммирования:

template<bool...> struct BoolSequence{};  //just here to hold bools 
template<char...> struct CharSequence{};  //just here to hold chars 
template<typename T, char C> struct Contains; //generic 
template<char First, char... Cs, char Match> //not first specialization 
struct Contains<CharSequence<First, Cs...>,Match> : 
    Contains<CharSequence<Cs...>, Match>{};  //strip first and increase index 
template<char First, char... Cs>    //is first specialization 
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {}; 
template<char Match>       //not found specialization 
struct Contains<CharSequence<>,Match>: std::false_type{}; 

template<int I, typename T, typename U> 
struct MakeSequence;       //generic 
template<int I, bool... Bs, typename U> 
struct MakeSequence<I,BoolSequence<Bs...>, U>: //not last 
    MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{}; 
template<bool... Bs, typename U> 
struct MakeSequence<0,BoolSequence<Bs...>,U>{ //last 
    using Type = BoolSequence<Bs...>; 
}; 
template<typename T> struct BoolASCIITable; 
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{ 
    /* 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<int>(c)]; 
    } 
}; 
using Delims = CharSequence<'.',',',' ',':','\n'>; //list your custom delimiters here 
using Table = BoolASCIITable<typename MakeSequence<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 end 
    auto second = std::find_if(begin,end,Table{});  //find first delim or end 
    return 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; 
    } 
    return 0; 
} 

Вот живой пример: http://ideone.com/GKtkLQ

12

Раствор с помощью regex_token_iterator S:

#include <iostream> 
#include <regex> 
#include <string> 

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<string> vec(iter, end); 

    for (auto a : vec) 
    { 
     cout << a << endl; 
    } 
} 
0

Я сделал лексического анализатора/Tokenizer перед тем с использование только стандартных библиотек. Вот код:

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

using namespace std; 

string seps(string& s) { 
    if (!s.size()) return ""; 
    stringstream ss; 
    ss << s[0]; 
    for (int i = 1; i < s.size(); i++) { 
     ss << '|' << s[i]; 
    } 
    return ss.str(); 
} 

void Tokenize(string& str, vector<string>& tokens, const string& delimiters = " ") 
{ 
    seps(str); 

    // 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); 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    vector<string> t; 
    string s = "Tokens for everyone!"; 

    Tokenize(s, t, "|"); 

    for (auto c : t) 
     cout << c << endl; 

    system("pause"); 

    return 0; 
} 
14

Это простое решение для 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); 
    } 
} 

Попробуйте live!

4

Adam Pierce's answer обеспечивает токенизатор с ручным отбором, принимающий в const char*. Это немного более проблематично с итераторами, потому что incrementing a string's end iterator is undefined. Тем не менее, учитывая string str{ "The quick brown fox" } мы можем, конечно, сделать это:

auto start = find(cbegin(str), cend(str), ' '); 
vector<string> tokens{ string(cbegin(str), start) }; 

while (start != cend(str)) { 
    const auto finish = find(++start, cend(str), ' '); 

    tokens.push_back(string(start, finish)); 
    start = finish; 
} 

Live Example


Если вы хотите абстрактной сложности с использованием стандартных функций, так как On Freund suggestsstrtok простой вариант:

vector<string> tokens; 

for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i); 

Если у вас нет доступа к C++ 17, вам необходимо будет заменить data(str) a с в этом примере: http://ideone.com/8kAGoa

Хотя в примере не показано, strtok не должен использовать один и тот же разделитель для каждого токена. Наряду с этим преимуществом, хотя, есть несколько недостатков:

  1. strtok не может использоваться на нескольких strings одновременно: Либо nullptr должен быть принят, чтобы продолжить tokenizing текущего string или новый char* к разметить должен быть передан (есть некоторые нестандартные реализации, которые действительно поддерживают это, однако, такие как: strtok_s)
  2. по той же причине strtok не могут быть использованы на нескольких потоков одновременно (это может быть, однако, осуществление определенных, например: Visual Studio's implementation is thread safe)
  3. Вызова strtok изменяет string он работает на, поэтому он не может быть использован на const string с, const char* с или текстовыми строками, чтобы разметить любого из них с strtok или работать на string кто есть содержание должно быть сохранено, str бы для копирования, то копия может работать на

Обе предыдущие методы не могут генерировать на месте в vector разбивается на лексемы, то есть без абстрагирования их во вспомогательную функцию, они не могут инициализировать const vector<string> tokens. Эта функциональность и возможность принимать любой разделитель пробелов можно использовать с помощью istream_iterator. Например Дано: const string str{ "The quick \tbrown \nfox" } мы можем сделать это:

istringstream is{ str }; 
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() }; 

Live Example

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


Если ни один из вышеперечисленных вариантов не достаточно flexable для ваших нужд лексемизации, наиболее гибкий вариант использует regex_token_iterator конечно с этой гибкостью приходит больший расход, но опять-таки это, скорее всего, скрытый в стоимости на string перерасчета. Скажем, например, мы хотим, чтобы разметить на основе не-сбежавших запятыми, также едят Пробельные, учитывая следующие входные: const string str{ "The ,qu\\,ick ,\tbrown, fox" } мы можем сделать это:

const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" }; 
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() }; 

Live Example