Вопрос

В Java есть удобный метод разделения:

String str = "The quick brown fox";
String[] results = str.split(" ");

Есть ли простой способ сделать это на C++?

Это было полезно?

Решение

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

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

А Токенизатор повышения class может сделать подобные вещи довольно простыми:

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

Обновлено для С++ 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;
    }
}

Вот очень простой:

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

Используйте стрток.По моему мнению, нет необходимости создавать класс для токенизации, если только 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 помещаются в разделители.Правильное использование может потребовать от вас создания неконстантной версии строки.Вы также можете изменить список разделителей в середине анализа.

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

Еще один быстрый способ — использовать getline.Что-то вроде:

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

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

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

Для этого можно использовать потоки, итераторы и алгоритм копирования.

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

Без обид, ребята, но для такой простой проблемы вы делаете вещи способ слишком сложно.Есть много причин использовать Способствовать росту.Но для такой простой вещи это все равно, что сбить муху санями 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] );
}

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

Ссылка: http://www.cplusplus.com/reference/string/string/.

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

Способствовать росту имеет сильную функцию разделения: повышение::алгоритм::сплит.

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

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

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

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

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

Qt

#include <QString>

...

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

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

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

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

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

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

Попробуйте это жить!

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

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

Вот пример того, как его использовать, который я уже публиковал в stackoverflow.

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

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

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

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

       std::string 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

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

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

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

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

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

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

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 (карты ввода-вывода, камеры).Я никогда не использую эту функцию для чего-то более сложного или критичного по времени, чем чтение внешних файлов конфигурации при запуске.

Здесь много слишком сложных предложений.Попробуйте это простое решение 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;
}

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

string word; sin >> word;

Ответ Адама Пирса предоставляет вращающийся вручную токенизатор, принимающий const char*.С итераторами работать немного сложнее, потому что увеличение stringконечный итератор не определен.Тем не менее, учитывая 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;
}

Живой пример


Если вы хотите абстрагировать сложность, используя стандартные функции, например Он Фрейнд предполагает 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 не нужно использовать один и тот же разделитель для каждого токена.Однако, наряду с этим преимуществом, есть и ряд недостатков:

  1. strtok нельзя использовать на нескольких strings в то же время:Либо nullptr необходимо передать, чтобы продолжить токенизацию текущего string или новый char* для токенизации необходимо передать (однако существуют некоторые нестандартные реализации, которые поддерживают это, например: strtok_s)
  2. По той же причине strtok нельзя использовать в нескольких потоках одновременно (однако это может быть определено реализацией, например: Реализация Visual Studio является потокобезопасной.)
  3. Вызов strtok изменяет string он работает, поэтому его нельзя использовать на const stringс, const char*s или литеральные строки, чтобы обозначить любой из них с помощью 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>() };

Живой пример

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


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

Живой пример

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

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

Некоторые уже предложили регулярное выражение, но для новичков вот упакованный пример, который должен делать именно то, что ожидает ОП:

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

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

vector<string> get_words(string const& text)
{
    vector<string> result;
    string tmp = text;

    size_t first_pos = 0;
    size_t second_pos = tmp.find(" ");;

    while (second_pos != string::npos)
    {
        if (first_pos != second_pos)
        {
            string word = tmp.substr(first_pos, second_pos - first_pos);
            result.push_back(word);
        }
        tmp = tmp.substr(second_pos + 1);
        second_pos = tmp.find(" ");
    }

    result.push_back(tmp);

    return result;
}

Прокомментируйте, есть ли в моем коде лучший подход к чему-либо или что-то не так.

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

вы можете воспользоваться 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;
}

Если известна максимальная длина входной строки, подлежащей токенизации, можно воспользоваться этим и реализовать очень быструю версию.Ниже я обрисовываю основную идею, которая была вдохновлена ​​как функцией strtok(), так и структурой данных «суффиксный массив», описанной в книге Джона Бентли «Программирование на Perls», глава 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;
    }
};

Файл реализации:

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

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

Простой код 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 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top