Самый элегантный способ повторения слов строки

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

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

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

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

using namespace std;

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

    do
    {
        string subs;
        iss >> subs;
        cout << "Substring: " << subs << endl;
    } while (iss);
}
 
вопрос задан 25.10.2008
Ashwin Nanjappa
34052 репутация

74 ответов


  • 2323 рейтинг

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

     #include <string>
    #include <sstream>
    #include <vector>
    #include <iterator>
    
    template<typename Out>
    void split(const std::string &s, char delim, Out result) {
        std::stringstream ss(s);
        std::string item;
        while (std::getline(ss, item, delim)) {
            *(result++) = item;
        }
    }
    
    std::vector<std::string> split(const std::string &s, char delim) {
        std::vector<std::string> elems;
        split(s, delim, std::back_inserter(elems));
        return elems;
    }
     

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

     std::vector<std::string> x = split("one:two::three", ':');
     
    ответ дан Evan Teran, с репутацией 65232, 25.10.2008
  • 1206 рейтинг

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

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

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

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

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

     vector<string> tokens{istream_iterator<string>{iss},
                          istream_iterator<string>{}};
     
    ответ дан Zunino, с репутацией 1950, 26.10.2008
  • 798 рейтинг

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

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

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

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

    ответ дан ididak, с репутацией 5042, 25.10.2008
  • 328 рейтинг
    #include <vector>
    #include <string>
    #include <sstream>
    
    int main()
    {
        std::string str("Split me by whitespaces");
        std::string buf;                 // Have a buffer string
        std::stringstream ss(str);       // Insert the string into a stream
    
        std::vector<std::string> tokens; // Create vector to hold our words
    
        while (ss >> buf)
            tokens.push_back(buf);
    
        return 0;
    }
    
    ответ дан kev, с репутацией 98981, 6.03.2011
  • 170 рейтинг

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

     template < class ContainerT >
    void tokenize(const std::string& str, ContainerT& tokens,
                  const std::string& delimiters = " ", bool trimEmpty = false)
    {
       std::string::size_type pos, lastPos = 0, length = str.length();
    
       using value_type = typename ContainerT::value_type;
       using size_type  = typename ContainerT::size_type;
    
       while(lastPos < length + 1)
       {
          pos = str.find_first_of(delimiters, lastPos);
          if(pos == std::string::npos)
          {
             pos = length;
          }
    
          if(pos != lastPos || !trimEmpty)
             tokens.push_back(value_type(str.data()+lastPos,
                   (size_type)pos-lastPos ));
    
          lastPos = pos + 1;
       }
    }
     

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

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

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

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

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

    ответ дан Marius, с репутацией 2747, 29.09.2009
  • 153 рейтинг

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

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

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

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

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

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

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

     std::vector<std::string> split(const std::string& text, const std::string& delims)
    {
        std::vector<std::string> tokens;
        std::size_t start = text.find_first_not_of(delims), end = 0;
    
        while((end = text.find_first_of(delims, start)) != std::string::npos)
        {
            tokens.push_back(text.substr(start, end - start));
            start = text.find_first_not_of(delims, end);
        }
        if(start != std::string::npos)
            tokens.push_back(text.substr(start));
    
        return tokens;
    }
     
    ответ дан Alec Thomas, с репутацией 11247, 13.09.2011
  • 109 рейтинг

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

     string line = "a line of text to iterate through";
    string word;
    
    istringstream iss(line, istringstream::in);
    
    while( iss >> word )     
    {
        // Do something on `word` here...
    }
     
    ответ дан gnomed, с репутацией 3587, 25.10.2008
  • 77 рейтинг

    Это похоже на вопрос о переполнении стека. Как я могу токенизировать строку в C ++? ,

     #include <iostream>
    #include <string>
    #include <boost/tokenizer.hpp>
    
    using namespace std;
    using namespace boost;
    
    int main(int argc, char** argv)
    {
        string text = "token  test\tstring";
    
        char_separator<char> sep(" \t");
        tokenizer<char_separator<char>> tokens(text, sep);
        for (const string& t : tokens)
        {
            cout << t << "." << endl;
        }
    }
     
    ответ дан Ferruccio, с репутацией 78508, 25.10.2008
  • 66 рейтинг

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

     #include <ostream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <iterator>
    using namespace std;
    
    vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
        vector<string> result;
        if (delim.empty()) {
            result.push_back(s);
            return result;
        }
        string::const_iterator substart = s.begin(), subend;
        while (true) {
            subend = search(substart, s.end(), delim.begin(), delim.end());
            string temp(substart, subend);
            if (keep_empty || !temp.empty()) {
                result.push_back(temp);
            }
            if (subend == s.end()) {
                break;
            }
            substart = subend + delim.size();
        }
        return result;
    }
    
    int main() {
        const vector<string> words = split("So close no matter how far", " ");
        copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
    }
     

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

    ответ дан Shadow2531, с репутацией 10293, 25.10.2008
  • 49 рейтинг

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

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

     void Tokenize(const string& str,
                  vector<string>& tokens,
                  const string& delimiters = " ")
    {
        // Skip delimiters at beginning.
        string::size_type lastPos = str.find_first_not_of(delimiters, 0);
        // Find first "non-delimiter".
        string::size_type pos     = str.find_first_of(delimiters, lastPos);
    
        while (string::npos != pos || string::npos != lastPos)
        {
            // Found a token, add it to the vector.
            tokens.push_back(str.substr(lastPos, pos - lastPos));
            // Skip delimiters.  Note the "not_of"
            lastPos = str.find_first_not_of(delimiters, pos);
            // Find next "non-delimiter"
            pos = str.find_first_of(delimiters, lastPos);
        }
    }
     

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

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

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

    Не продавайте эту сделку «Elegance over performance» .

    ответ дан nlaq, с репутацией 19470, 25.10.2008
  • 39 рейтинг

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

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

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

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

         vector<string> v = split<string>("Hello, there; World", ";,");
        vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");
     
    ответ дан Marco M., с репутацией 1475, 13.03.2012
  • 33 рейтинг

    У меня есть 2-строчное решение этой проблемы:

     char sep = ' ';
    std::string s="1 This is an example";
    
    for(size_t p=0, q=0; p!=s.npos; p=q)
      std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;
     

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

    ответ дан rhomu, с репутацией 61, 16.09.2012
  • 33 рейтинг

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

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

    Использовать его с вектором строк (Edit: Поскольку кто-то указал, что не наследует классы STL ... hrmf;)):

     template<class ContainerType>
    class Appender {
    public:
      Appender(ContainerType& container) : container_(container) {;}
      void operator() (const char* s, unsigned length) { 
        container_.push_back(std::string(s,length));
      }
    private:
      ContainerType& container_;
    };
    
    std::vector<std::string> strVector;
    Appender v(strVector);
    tokenize(v, "A number of words to be tokenized", " \t");
     

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

     class WordCounter {
    public:
      WordCounter() : noOfWords(0) {}
      void operator() (const char*, unsigned) {
        ++noOfWords;
      }
      unsigned noOfWords;
    };
    
    WordCounter wc;
    tokenize(wc, "A number of words to be counted", " \t"); 
    ASSERT( wc.noOfWords == 7 );
     

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

    ответ дан Robert, с репутацией 1340, 1.04.2010
  • 29 рейтинг

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

     #include <regex>
    #include <string>
    #include <vector>
    
    std::vector<string> Tokenize( const string str, const std::regex regex )
    {
        using namespace std;
    
        std::vector<string> result;
    
        sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
        sregex_token_iterator reg_end;
    
        for ( ; it != reg_end; ++it ) {
            if ( !it->str().empty() ) //token could be empty:check
                result.emplace_back( it->str() );
        }
    
        return result;
    }
     

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

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

     std::vector<string> TokenizeDefault( const string str )
    {
        using namespace std;
    
        regex re( "[\\s,]+" );
    
        return Tokenize( str, re );
    }
     

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

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

    • изменить все std::regex до std::wregex
    • изменить все sregex_token_iterator до wsregex_token_iterator

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

    ответ дан dk123, с репутацией 7869, 6.05.2014
  • 24 рейтинг

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

    Пример кода, включая удобный шаблон:

     #include <iostream>
    #include <vector>
    #include <boost/algorithm/string.hpp>
    
    template<typename _OutputIterator>
    inline void split(
        const std::string& str, 
        const std::string& delim, 
        _OutputIterator result)
    {
        using namespace boost::algorithm;
        typedef split_iterator<std::string::const_iterator> It;
    
        for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
                iter!=It();
                ++iter)
        {
            *(result++) = boost::copy_range<std::string>(*iter);
        }
    }
    
    int main(int argc, char* argv[])
    {
        using namespace std;
    
        vector<string> splitted;
        split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));
    
        // or directly to console, for example
        split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
        return 0;
    }
     
    ответ дан zerm, с репутацией 2390, 24.03.2011
  • 23 рейтинг

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

    Вот пример:

     #include <iostream>
    #include <string>
    
    int main()
    {
        std::string s("Somewhere down the road");
        std::string::size_type prev_pos = 0, pos = 0;
    
        while( (pos = s.find(' ', pos)) != std::string::npos )
        {
            std::string substring( s.substr(prev_pos, pos-prev_pos) );
    
            std::cout << substring << '\n';
    
            prev_pos = ++pos;
        }
    
        std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
        std::cout << substring << '\n';
    
        return 0;
    }
     
    ответ дан KTC, с репутацией 8288, 25.10.2008
  • 18 рейтинг

    Существует функция с именем strtok .

     #include<string>
    using namespace std;
    
    vector<string> split(char* str,const char* delim)
    {
        char* saveptr;
        char* token = strtok_r(str,delim,&saveptr);
    
        vector<string> result;
    
        while(token != NULL)
        {
            result.push_back(token);
            token = strtok_r(NULL,delim,&saveptr);
        }
        return result;
    }
     
    ответ дан Pratik Deoghare, с репутацией 17000, 14.06.2010
  • 17 рейтинг

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

     #include <regex.h>
    #include <string.h>
    #include <vector.h>
    
    using namespace std;
    
    vector<string> split(string s){
        regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
        regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
        regex_iterator<string::iterator> rend; //iterators to iterate thru words
        vector<string> result<regex_iterator>(rit, rend);
        return result;  //iterates through the matches to fill the vector
    }
     
    ответ дан AJMansfield, с репутацией 2796, 29.10.2012
  • 15 рейтинг

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

     string s = "Name:JAck; Spouse:Susan; ...";
    string dummy, name, spouse;
    
    istringstream iss(s);
    getline(iss, dummy, ':');
    getline(iss, name, ';');
    getline(iss, dummy, ':');
    getline(iss, spouse, ';')
     
    ответ дан lukmac, с репутацией 1866, 12.08.2011
  • 14 рейтинг

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

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

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

    ответ дан Goran, с репутацией 68, 14.01.2011
  • 13 рейтинг

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

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

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

    с помощью:

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

    вывод:

     this
    is
    !example!string
     
    ответ дан user1438233, с репутацией 708, 2.11.2015
  • 13 рейтинг

    Я развернул свой собственный, используя strtok, и использовал boost для разделения строки. Лучший метод, который я нашел, это 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
            std::string s("Somewhere down the road");
            std::vector<std::string> result;
            if( strtk::parse( s, whitespace, result ) )
            {
                for(size_t i = 0; i < result.size(); ++i )
                    std::cout << result[i] << std::endl;
            }
        }
    
        {  // parsing a string into a vector of floats with other separators
            // besides spaces
    
            std::string s("3.0, 3.14; 4.0");
            std::vector<float> values;
            if( strtk::parse( s, whitespace_and_punctuation, values ) )
            {
                for(size_t i = 0; i < values.size(); ++i )
                    std::cout << values[i] << std::endl;
            }
        }
    
        {  // parsing a string into specific variables
    
            std::string s("angle = 45; radius = 9.9");
            std::string w1, w2;
            float v1, v2;
            if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
            {
                std::cout << "word " << w1 << ", value " << v1 << std::endl;
                std::cout << "word " << w2 << ", value " << v2 << std::endl;
            }
        }
    
        return 0;
    }
     

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

    ответ дан DannyK, с репутацией 1148, 7.01.2014
  • 11 рейтинг

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

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

    StringSplitter.hpp:

     #include <vector>
    #include <iostream>
    #include <string.h>
    
    using namespace std;
    
    class StringSplit
    {
    private:
        void copy_fragment(char*, char*, char*);
        void copy_fragment(char*, char*, char);
        bool match_fragment(char*, char*, int);
        int untilnextdelim(char*, char);
        int untilnextdelim(char*, char*);
        void assimilate(char*, char);
        void assimilate(char*, char*);
        bool string_contains(char*, char*);
        long calc_string_size(char*);
        void copy_string(char*, char*);
    
    public:
        vector<char*> split_cstr(char);
        vector<char*> split_cstr(char*);
        vector<string> split_string(char);
        vector<string> split_string(char*);
        char* String;
        bool do_string;
        bool keep_empty;
        vector<char*> Container;
        vector<string> ContainerS;
    
        StringSplit(char * in)
        {
            String = in;
        }
    
        StringSplit(string in)
        {
            size_t len = calc_string_size((char*)in.c_str());
            String = new char[len + 1];
            memset(String, 0, len + 1);
            copy_string(String, (char*)in.c_str());
            do_string = true;
        }
    
        ~StringSplit()
        {
            for (int i = 0; i < Container.size(); i++)
            {
                if (Container[i] != NULL)
                {
                    delete[] Container[i];
                }
            }
            if (do_string)
            {
                delete[] String;
            }
        }
    };
     

    StringSplitter.cpp:

     #include <string.h>
    #include <iostream>
    #include <vector>
    #include "StringSplit.hpp"
    
    using namespace std;
    
    void StringSplit::assimilate(char*src, char delim)
    {
        int until = untilnextdelim(src, delim);
        if (until > 0)
        {
            char * temp = new char[until + 1];
            memset(temp, 0, until + 1);
            copy_fragment(temp, src, delim);
            if (keep_empty || *temp != 0)
            {
                if (!do_string)
                {
                    Container.push_back(temp);
                }
                else
                {
                    string x = temp;
                    ContainerS.push_back(x);
                }
    
            }
            else
            {
                delete[] temp;
            }
        }
    }
    
    void StringSplit::assimilate(char*src, char* delim)
    {
        int until = untilnextdelim(src, delim);
        if (until > 0)
        {
            char * temp = new char[until + 1];
            memset(temp, 0, until + 1);
            copy_fragment(temp, src, delim);
            if (keep_empty || *temp != 0)
            {
                if (!do_string)
                {
                    Container.push_back(temp);
                }
                else
                {
                    string x = temp;
                    ContainerS.push_back(x);
                }
            }
            else
            {
                delete[] temp;
            }
        }
    }
    
    long StringSplit::calc_string_size(char* _in)
    {
        long i = 0;
        while (*_in++)
        {
            i++;
        }
        return i;
    }
    
    bool StringSplit::string_contains(char* haystack, char* needle)
    {
        size_t len = calc_string_size(needle);
        size_t lenh = calc_string_size(haystack);
        while (lenh--)
        {
            if (match_fragment(haystack + lenh, needle, len))
            {
                return true;
            }
        }
        return false;
    }
    
    bool StringSplit::match_fragment(char* _src, char* cmp, int len)
    {
        while (len--)
        {
            if (*(_src + len) != *(cmp + len))
            {
                return false;
            }
        }
        return true;
    }
    
    int StringSplit::untilnextdelim(char* _in, char delim)
    {
        size_t len = calc_string_size(_in);
        if (*_in == delim)
        {
            _in += 1;
            return len - 1;
        }
    
        int c = 0;
        while (*(_in + c) != delim && c < len)
        {
            c++;
        }
    
        return c;
    }
    
    int StringSplit::untilnextdelim(char* _in, char* delim)
    {
        int s = calc_string_size(delim);
        int c = 1 + s;
    
        if (!string_contains(_in, delim))
        {
            return calc_string_size(_in);
        }
        else if (match_fragment(_in, delim, s))
        {
            _in += s;
            return calc_string_size(_in);
        }
    
        while (!match_fragment(_in + c, delim, s))
        {
            c++;
        }
    
        return c;
    }
    
    void StringSplit::copy_fragment(char* dest, char* src, char delim)
    {
        if (*src == delim)
        {
            src++;
        }
    
        int c = 0;
        while (*(src + c) != delim && *(src + c))
        {
            *(dest + c) = *(src + c);
            c++;
        }
        *(dest + c) = 0;
    }
    
    void StringSplit::copy_string(char* dest, char* src)
    {
        int i = 0;
        while (*(src + i))
        {
            *(dest + i) = *(src + i);
            i++;
        }
    }
    
    void StringSplit::copy_fragment(char* dest, char* src, char* delim)
    {
        size_t len = calc_string_size(delim);
        size_t lens = calc_string_size(src);
    
        if (match_fragment(src, delim, len))
        {
            src += len;
            lens -= len;
        }
    
        int c = 0;
        while (!match_fragment(src + c, delim, len) && (c < lens))
        {
            *(dest + c) = *(src + c);
            c++;
        }
        *(dest + c) = 0;
    }
    
    vector<char*> StringSplit::split_cstr(char Delimiter)
    {
        int i = 0;
        while (*String)
        {
            if (*String != Delimiter && i == 0)
            {
                assimilate(String, Delimiter);
            }
            if (*String == Delimiter)
            {
                assimilate(String, Delimiter);
            }
            i++;
            String++;
        }
    
        String -= i;
        delete[] String;
    
        return Container;
    }
    
    vector<string> StringSplit::split_string(char Delimiter)
    {
        do_string = true;
    
        int i = 0;
        while (*String)
        {
            if (*String != Delimiter && i == 0)
            {
                assimilate(String, Delimiter);
            }
            if (*String == Delimiter)
            {
                assimilate(String, Delimiter);
            }
            i++;
            String++;
        }
    
        String -= i;
        delete[] String;
    
        return ContainerS;
    }
    
    vector<char*> StringSplit::split_cstr(char* Delimiter)
    {
        int i = 0;
        size_t LenDelim = calc_string_size(Delimiter);
    
        while(*String)
        {
            if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
            {
                assimilate(String, Delimiter);
            }
            if (match_fragment(String, Delimiter, LenDelim))
            {
                assimilate(String,Delimiter);
            }
            i++;
            String++;
        }
    
        String -= i;
        delete[] String;
    
        return Container;
    }
    
    vector<string> StringSplit::split_string(char* Delimiter)
    {
        do_string = true;
        int i = 0;
        size_t LenDelim = calc_string_size(Delimiter);
    
        while (*String)
        {
            if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
            {
                assimilate(String, Delimiter);
            }
            if (match_fragment(String, Delimiter, LenDelim))
            {
                assimilate(String, Delimiter);
            }
            i++;
            String++;
        }
    
        String -= i;
        delete[] String;
    
        return ContainerS;
    }
     

    Примеры:

     int main(int argc, char*argv[])
    {
        StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
        vector<char*> Split = ss.split_cstr(":CUT:");
    
        for (int i = 0; i < Split.size(); i++)
        {
            cout << Split[i] << endl;
        }
    
        return 0;
    }
     

    Вывод:

    Эта
    является

    пример
    CString

     int main(int argc, char*argv[])
    {
        StringSplit ss = "This:is:an:example:cstring";
        vector<char*> Split = ss.split_cstr(':');
    
        for (int i = 0; i < Split.size(); i++)
        {
            cout << Split[i] << endl;
        }
    
        return 0;
    }
    
    int main(int argc, char*argv[])
    {
        string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
        StringSplit ss = mystring;
        vector<string> Split = ss.split_string("[SPLIT]");
    
        for (int i = 0; i < Split.size(); i++)
        {
            cout << Split[i] << endl;
        }
    
        return 0;
    }
    
    int main(int argc, char*argv[])
    {
        string mystring = "This|is|an|example|string";
        StringSplit ss = mystring;
        vector<string> Split = ss.split_string('|');
    
        for (int i = 0; i < Split.size(); i++)
        {
            cout << Split[i] << endl;
        }
    
        return 0;
    }
     

    Сохранять пустые записи (по умолчанию исключаются пустые записи):

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

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

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

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

    ответ дан Steve Dell, с репутацией 308, 31.08.2012
  • 10 рейтинг

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

     #include <string>
    #include <vector>
    
    using namespace std;
    
    vector<string> split(string str, const char delim) {
        vector<string> v;
        string tmp;
    
        for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
            if(*i != delim && i != str.end()) {
                tmp += *i; 
            } else {
                v.push_back(tmp);
                tmp = ""; 
            }   
        }   
    
        return v;
    }
     
    ответ дан gibbz, с репутацией 11, 23.06.2011
  • 9 рейтинг

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

     #include <boost/algorithm/string.hpp>
    std::vector<std::string> strs;
    boost::split(strs, "string to split", boost::is_any_of("\t "));
     
    ответ дан NL628, с репутацией 303, 9.12.2017
  • 9 рейтинг

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

     #include <iostream>
    #include <string>
    #include <boost/regex.hpp>
    
    int main() {
        std::string line("A:::line::to:split");
        const boost::regex re(":+"); // one or more colons
    
        // -1 means find inverse matches aka split
        boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
        boost::sregex_token_iterator end;
    
        for (; tokens != end; ++tokens)
            std::cout << *tokens << std::endl;
    }
     
    ответ дан Marty B, с репутацией 178, 12.06.2011
  • 9 рейтинг

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

     void split_string(string text,vector<string>& words)
    {
      int i=0;
      char ch;
      string word;
    
      while(ch=text[i++])
      {
        if (isspace(ch))
        {
          if (!word.empty())
          {
            words.push_back(word);
          }
          word = "";
        }
        else
        {
          word += ch;
        }
      }
      if (!word.empty())
      {
        words.push_back(word);
      }
    }
     
    ответ дан Marius, с репутацией 2747, 8.01.2010
  • 9 рейтинг

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

     #include <string>
    #include <list>
    #include <locale> // std::isupper
    
    template<class String>
    const std::list<String> split_camel_case_string(const String &s)
    {
        std::list<String> R;
        String w;
    
        for (String::const_iterator i = s.begin(); i < s.end(); ++i) {  {
            if (std::isupper(*i)) {
                if (w.length()) {
                    R.push_back(w);
                    w.clear();
                }
            }
            w += *i;
        }
    
        if (w.length())
            R.push_back(w);
        return R;
    }
     

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

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

    ответ дан Andreas Spindler, с репутацией 4174, 14.09.2011
  • 8 рейтинг

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

     #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    
    char one_line_string[] = "hello hi how are you nice weather we are having ok then bye";
    char seps[]   = " ,\t\n";
    char *token;
    
    
    
    int main()
    {
       vector<string> vec_String_Lines;
       token = strtok( one_line_string, seps );
    
       cout << "Extracting and storing data in a vector..\n\n\n";
    
       while( token != NULL )
       {
          vec_String_Lines.push_back(token);
          token = strtok( NULL, seps );
       }
         cout << "Displaying end result in vector line storage..\n\n";
    
        for ( int i = 0; i < vec_String_Lines.size(); ++i)
        cout << vec_String_Lines[i] << "\n";
        cout << "\n\n\n";
    
    
    return 0;
    }
     
    ответ дан Software_Designer, с репутацией 6450, 10.12.2011
  • 8 рейтинг
    #include<iostream>
    #include<string>
    #include<sstream>
    #include<vector>
    using namespace std;
    
        vector<string> split(const string &s, char delim) {
            vector<string> elems;
            stringstream ss(s);
            string item;
            while (getline(ss, item, delim)) {
                elems.push_back(item);
            }
            return elems;
        }
    
    int main() {
    
            vector<string> x = split("thi is an sample test",' ');
            unsigned int i;
            for(i=0;i<x.size();i++)
                cout<<i<<":"<<x[i]<<endl;
            return 0;
    }
    
    ответ дан san45, с репутацией 181, 3.10.2013
  • 8 рейтинг

    Получить Boost ! : -)

     #include <boost/algorithm/string/split.hpp>
    #include <boost/algorithm/string.hpp>
    #include <iostream>
    #include <vector>
    
    using namespace std;
    using namespace boost;
    
    int main(int argc, char**argv) {
        typedef vector < string > list_type;
    
        list_type list;
        string line;
    
        line = "Somewhere down the road";
        split(list, line, is_any_of(" "));
    
        for(int i = 0; i < list.size(); i++)
        {
            cout << list[i] << endl;
        }
    
        return 0;
    }
     

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

     Somewhere
    down
    the
    road
     
    ответ дан Aleksey Bykov, с репутацией 6761, 7.04.2013
  • 7 рейтинг

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

     void splitString(const String &s, const String &delim, std::vector<String> &result) {
        const int l = delim.length();
        int f = 0;
        int i = s.indexOf(delim,f);
        while (i>=0) {
            String token( i-f > 0 ? s.substring(f,i-f) : "");
            result.push_back(token);
            f=i+l;
            i = s.indexOf(delim,f);
        }
        String token = s.substring(f);
        result.push_back(token);
    }
     
    ответ дан Abe, с репутацией 31, 1.09.2010
  • 6 рейтинг
     #include <iostream>
    #include <regex>
    
    using namespace std;
    
    int main() {
       string s = "foo bar  baz";
       regex e("\\s+");
       regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
       regex_token_iterator<string::iterator> end;
       while (i != end)
          cout << " [" << *i++ << "]";
    }
     

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

    ответ дан solstice333, с репутацией 1063, 6.09.2016
  • 6 рейтинг

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

     #pragma once
    #include <vector>
    #include <sstream>
    using namespace std;
    class Helpers
    {
        public:
            static vector<string> split(string s, char delim)
            {
                stringstream temp (stringstream::in | stringstream::out);
                vector<string> elems(0);
                if (s.size() == 0 || delim == 0)
                    return elems;
                for(char c : s)
                {
                    if(c == delim)
                    {
                        elems.push_back(temp.str());
                        temp = stringstream(stringstream::in | stringstream::out);
                    }
                    else
                        temp << c;
                }
                if (temp.str().size() > 0)
                    elems.push_back(temp.str());
                    return elems;
                }
    
            //Splits string s with a list of delimiters in delims (it's just a list, like if we wanted to
            //split at the following letters, a, b, c we would make delims="abc".
            static vector<string> split(string s, string delims)
            {
                stringstream temp (stringstream::in | stringstream::out);
                vector<string> elems(0);
                bool found;
                if(s.size() == 0 || delims.size() == 0)
                    return elems;
                for(char c : s)
                {
                    found = false;
                    for(char d : delims)
                    {
                        if (c == d)
                        {
                            elems.push_back(temp.str());
                            temp = stringstream(stringstream::in | stringstream::out);
                            found = true;
                            break;
                        }
                    }
                    if(!found)
                        temp << c;
                }
                if(temp.str().size() > 0)
                    elems.push_back(temp.str());
                return elems;
            }
    };
     
    ответ дан Kelly Elton, с репутацией 2288, 15.05.2011
  • 5 рейтинг

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

     #include <iostream>
    #include <string>
    #include <vector>
    
    std::vector<std::string> split(const std::string &s, const std::string &delims)
    {
        std::vector<std::string> result;
        std::string::size_type pos = 0;
        while (std::string::npos != (pos = s.find_first_not_of(delims, pos))) {
            auto pos2 = s.find_first_of(delims, pos);
            result.emplace_back(s.substr(pos, std::string::npos == pos2 ? pos2 : pos2 - pos));
            pos = pos2;
        }
        return result;
    }
    
    int main()
    {
        std::string text{"And then I said: \"I don't get it, why would you even do that!?\""};
        std::string delims{" :;\".,?!"};
        auto words = split(text, delims);
        std::cout << "\nSentence:\n  " << text << "\n\nWords:";
        for (const auto &w : words) {
            std::cout << "\n  " << w;
        }
        return 0;
    }
     

    http://cpp.sh/7wmzy

    ответ дан Jehjoa, с репутацией 196, 2.09.2015
  • 5 рейтинг

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

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

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

    Код:

     vector <string> split(const string& str, const string& delimiter = " ") {
        vector <string> tokens;
    
        string::size_type lastPos = 0;
        string::size_type pos = str.find(delimiter, lastPos);
    
        while (string::npos != pos) {
            // Found a token, add it to the vector.
            cout << str.substr(lastPos, pos - lastPos) << endl;
            tokens.push_back(str.substr(lastPos, pos - lastPos));
            lastPos = pos + delimiter.size();
            pos = str.find(delimiter, lastPos);
        }
    
        tokens.push_back(str.substr(lastPos, str.size() - lastPos));
        return tokens;
    }
     
    ответ дан Jim Huang, с репутацией 376, 7.10.2012
  • 5 рейтинг

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

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

     #include <iostream>
    #include <algorithm>
    #include <iterator>
    #include <sstream>
    #include <locale>
    
    struct whitespace_mask {
        std::ctype_base::mask mask_table[std::ctype<char>::table_size];
        whitespace_mask(std::string const& spaces) {
            std::ctype_base::mask* table = this->mask_table;
            std::ctype_base::mask const* tab
                = std::use_facet<std::ctype<char>>(std::locale()).table();
            for (std::size_t i(0); i != std::ctype<char>::table_size; ++i) {
                table[i] = tab[i] & ~std::ctype_base::space;
            }
            std::for_each(spaces.begin(), spaces.end(), [=](unsigned char c) {
                table[c] |= std::ctype_base::space;
            });
        }
    };
    class whitespace_facet
        : private whitespace_mask
        , public std::ctype<char> {
    public:
        whitespace_facet(std::string const& spaces)
            : whitespace_mask(spaces)
            , std::ctype<char>(this->mask_table) {
        }
    };
    
    struct whitespace {
        std::string spaces;
        whitespace(std::string const& spaces): spaces(spaces) {}
    };
    std::istream& operator>>(std::istream& in, whitespace const& ws) {
        std::locale loc(in.getloc(), new whitespace_facet(ws.spaces));
        in.imbue(loc);
        return in;
    }
    // everything above would probably go into a utility library...
    
    int main() {
        std::istringstream in("a, b, c, d, e");
        std::copy(std::istream_iterator<std::string>(in >> whitespace(", ")),
                  std::istream_iterator<std::string>(),
                  std::ostream_iterator<std::string>(std::cout, "\n"));
    
        std::istringstream pipes("a b c|  d |e     e");
        std::copy(std::istream_iterator<std::string>(pipes >> whitespace("|")),
                  std::istream_iterator<std::string>(),
                  std::ostream_iterator<std::string>(std::cout, "\n"));   
    }
     

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

     void f(std::istream& in) {
        std::istream pipes(in.rdbuf());
        pipes >> whitespace("|");
        std::istream comma(in.rdbuf());
        comma >> whitespace(",");
    
        std::string s0, s1;
        if (pipes >> s0 >> std::ws   // read up to first pipe and ignore sequence of pipes
            && comma >> s1 >> std::ws) { // read up to first comma and ignore commas
            // ...
        }
    }
     
    ответ дан Dietmar Kühl, с репутацией 123199, 18.12.2014
  • 4 рейтинг

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

     #include <vector>
    #include <string>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    
    std::vector<std::string> split(const std::string& s)
    {
        std::vector<std::string> v;
    
        const auto end = s.end();
        auto to = s.begin();
        decltype(to) from;
    
        while((from = std::find_if(to, end,
            [](char c){ return !std::isspace(c); })) != end)
        {
            to = std::find_if(from, end, [](char c){ return std::isspace(c); });
            v.emplace_back(from, to);
        }
    
        return v;
    }
    
    int main()
    {
        std::string s = "this is the string  to  split";
    
        auto v = split(s);
    
        for(auto&& s: v)
            std::cout << s << '\n';
    }
     

    Вывод:

     this
    is
    the
    string
    to
    split
     
    ответ дан Galik, с репутацией 31989, 25.11.2014
  • 4 рейтинг

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

     #include <string>
    #include <vector>
    void split(vector<string> &result, string str, char delim ) {
      string tmp;
      string::iterator i;
      result.clear();
    
      for(i = str.begin(); i <= str.end(); ++i) {
        if((const char)*i != delim  && i != str.end()) {
          tmp += *i;
        } else {
          result.push_back(tmp);
          tmp = "";
        }
      }
    }
     

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

     vector<string> hosts;
    split(hosts, "192.168.1.2,192.168.1.3", ',');
    for( size_t i = 0; i < hosts.size(); i++){
      cout <<  "Connecting host : " << hosts.at(i) << "..." << endl;
    }
     
    ответ дан Jairo Abdiel Toribio Cisneros, с репутацией 11, 28.07.2011
  • 4 рейтинг

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

     namespace Core
    {
        typedef std::wstring String;
    
        void SplitString(const Core::String& input, const Core::String& splitter, std::list<Core::String>& output)
        {
            if (splitter.empty())
            {
                throw std::invalid_argument(); // for example
            }
    
            std::list<Core::String> lines;
    
            Core::String::size_type offset = 0;
    
            for (;;)
            {
                Core::String::size_type splitterPos = input.find(splitter, offset);
    
                if (splitterPos != Core::String::npos)
                {
                    lines.push_back(input.substr(offset, splitterPos - offset));
                    offset = splitterPos + splitter.size();
                }
                else
                {
                    lines.push_back(input.substr(offset));
                    break;
                }
            }
    
            lines.swap(output);
        }
    }
    
    // gtest:
    
    class SplitStringTest: public testing::Test
    {
    };
    
    TEST_F(SplitStringTest, EmptyStringAndSplitter)
    {
        std::list<Core::String> result;
        ASSERT_ANY_THROW(Core::SplitString(Core::String(), Core::String(), result));
    }
    
    TEST_F(SplitStringTest, NonEmptyStringAndEmptySplitter)
    {
        std::list<Core::String> result;
        ASSERT_ANY_THROW(Core::SplitString(L"xy", Core::String(), result));
    }
    
    TEST_F(SplitStringTest, EmptyStringAndNonEmptySplitter)
    {
        std::list<Core::String> result;
        Core::SplitString(Core::String(), Core::String(L","), result);
        ASSERT_EQ(1, result.size());
        ASSERT_EQ(Core::String(), *result.begin());
    }
    
    TEST_F(SplitStringTest, OneCharSplitter)
    {
        std::list<Core::String> result;
    
        Core::SplitString(L"x,y", L",", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(L"x", *result.begin());
        ASSERT_EQ(L"y", *result.rbegin());
    
        Core::SplitString(L",xy", L",", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(Core::String(), *result.begin());
        ASSERT_EQ(L"xy", *result.rbegin());
    
        Core::SplitString(L"xy,", L",", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(L"xy", *result.begin());
        ASSERT_EQ(Core::String(), *result.rbegin());
    }
    
    TEST_F(SplitStringTest, TwoCharsSplitter)
    {
        std::list<Core::String> result;
    
        Core::SplitString(L"x,.y,z", L",.", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(L"x", *result.begin());
        ASSERT_EQ(L"y,z", *result.rbegin());
    
        Core::SplitString(L"x,,y,z", L",,", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(L"x", *result.begin());
        ASSERT_EQ(L"y,z", *result.rbegin());
    }
    
    TEST_F(SplitStringTest, RecursiveSplitter)
    {
        std::list<Core::String> result;
    
        Core::SplitString(L",,,", L",,", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(Core::String(), *result.begin());
        ASSERT_EQ(L",", *result.rbegin());
    
        Core::SplitString(L",.,.,", L",.,", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(Core::String(), *result.begin());
        ASSERT_EQ(L".,", *result.rbegin());
    
        Core::SplitString(L"x,.,.,y", L",.,", result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(L"x", *result.begin());
        ASSERT_EQ(L".,y", *result.rbegin());
    
        Core::SplitString(L",.,,.,", L",.,", result);
        ASSERT_EQ(3, result.size());
        ASSERT_EQ(Core::String(), *result.begin());
        ASSERT_EQ(Core::String(), *(++result.begin()));
        ASSERT_EQ(Core::String(), *result.rbegin());
    }
    
    TEST_F(SplitStringTest, NullTerminators)
    {
        std::list<Core::String> result;
    
        Core::SplitString(L"xy", Core::String(L"\0", 1), result);
        ASSERT_EQ(1, result.size());
        ASSERT_EQ(L"xy", *result.begin());
    
        Core::SplitString(Core::String(L"x\0y", 3), Core::String(L"\0", 1), result);
        ASSERT_EQ(2, result.size());
        ASSERT_EQ(L"x", *result.begin());
        ASSERT_EQ(L"y", *result.rbegin());
    }
     
    ответ дан Dmitry, с репутацией 1, 25.07.2012
  • 3 рейтинг

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

         // Split string into parts.
        class Split : public std::vector<std::string>
        {
            public:
                Split(const std::string& str, char* delimList)
                {
                   size_t lastPos = 0;
                   size_t pos = str.find_first_of(delimList);
    
                   while (pos != std::string::npos)
                   {
                        if (pos != lastPos)
                            push_back(str.substr(lastPos, pos-lastPos));
                        lastPos = pos + 1;
                        pos = str.find_first_of(delimList, lastPos);
                   }
                   if (lastPos < str.length())
                       push_back(str.substr(lastPos, pos-lastPos));
                }
        };
     

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

     std::set<std::string> words;
    Split split("Hello,World", ",");
    words.insert(split.begin(), split.end());
     
    ответ дан landen, с репутацией 1, 21.02.2012
  • 3 рейтинг

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

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

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

    ответ дан Pzy64, с репутацией 514, 5.09.2016
  • 2 рейтинг

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

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

     #include <string>
    #include <list>
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    const char *wschars = "\t\n ";
    
    list<string> split(const string &str)
    {
      const char *cstr = str.c_str();
      list<string> out;
    
      while (*cstr) {                     // while remaining string not empty
        size_t toklen;
        cstr += strspn(cstr, wschars);    // skip leading whitespace
        toklen = strcspn(cstr, wschars);  // figure out token length
        if (toklen)                       // if we have a token, add to list
          out.push_back(string(cstr, toklen));
        cstr += toklen;                   // skip over token
      }
    
      // ran out of string; return list
    
      return out;
    }
    
    int main(int argc, char **argv)
    {
      list<string> li = split(argv[1]);
      for (list<string>::iterator i = li.begin(); i != li.end(); i++)
        cout << "{" << *i << "}" << endl;
      return 0;
    }
     

    Бег:

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

    Хвост-рекурсивная версия split (сама разбита на две функции). Все разрушительные манипуляции с переменными исчезли, за исключением нажатия строк в список!

     void split_rec(const char *cstr, list<string> &li)
    {
      if (*cstr) {
        const size_t leadsp = strspn(cstr, wschars);
        const size_t toklen = strcspn(cstr + leadsp, wschars);
    
        if (toklen)
          li.push_back(string(cstr + leadsp, toklen));
    
        split_rec(cstr + leadsp + toklen, li);
      }
    }
    
    list<string> split(const string &str)
    {
      list<string> out;
      split_rec(str.c_str(), out);
      return out;
    }
     
    ответ дан Kaz, с репутацией 36860, 7.04.2014
  • 2 рейтинг

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

    Я не мог найти версию split которая работает для разделителей с несколькими символами, так что вот моя:

     #include <string>
    #include <vector>
    
    using namespace std;
    
    vector<string> split(const string &str, const string &delim)
    {   
        const auto delim_pos = str.find(delim);
    
        if (delim_pos == string::npos)
            return {str};
    
        vector<string> ret{str.substr(0, delim_pos)};
        auto tail = split(str.substr(delim_pos + delim.size(), string::npos), delim);
    
        ret.insert(ret.end(), tail.begin(), tail.end());
    
        return ret;
    }
     

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

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

    ответ дан Romário, с репутацией 733, 31.05.2017
  • 2 рейтинг

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

     #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main()
    {
        char str[]="Mickey M;12034;911416313;M;01a;9001;NULL;0;13;12;0;CPP,C;MSC,3D;FEND,BEND,SEC;";
        char *pch = strtok (str,";,");
        while (pch != NULL)
        {
            cout<<pch<<"\n";
            pch = strtok (NULL, ";,");
        }
        return 0;
    }
     
    ответ дан Venkata Naidu M, с репутацией 331, 3.06.2015
  • 2 рейтинг

    LazyStringSplitter:

     #include <string>
    #include <algorithm>
    #include <unordered_set>
    
    using namespace std;
    
    class LazyStringSplitter
    {
        string::const_iterator start, finish;
        unordered_set<char> chop;
    
    public:
    
        // Empty Constructor
        explicit LazyStringSplitter()
        {}
    
        explicit LazyStringSplitter (const string cstr, const string delims)
            : start(cstr.begin())
            , finish(cstr.end())
            , chop(delims.begin(), delims.end())
        {}
    
        void operator () (const string cstr, const string delims)
        {
            chop.insert(delims.begin(), delims.end());
            start = cstr.begin();
            finish = cstr.end();
        }
    
        bool empty() const { return (start >= finish); }
    
        string next()
        {
            // return empty string
            // if ran out of characters
            if (empty())
                return string("");
    
            auto runner = find_if(start, finish, [&](char c) {
                return chop.count(c) == 1;
            });
    
            // construct next string
            string ret(start, runner);
            start = runner + 1;
    
            // Never return empty string
            // + tail recursion makes this method efficient
            return !ret.empty() ? ret : next();
        }
    };
     
    • Я называю этот метод LazyStringSplitter по одной причине - он не разбивает строку за один раз.
    • По сути, он ведет себя как генератор питона
    • Он предоставляет метод с именем next который возвращает следующую строку, разделенную на оригинал
    • Я использовал unordered_set из c ++ 11 STL, так что поиск разделителей намного быстрее
    • И вот как это работает

    ИСПЫТАТЕЛЬНАЯ ПРОГРАММА

     #include <iostream>
    using namespace std;
    
    int main()
    {
        LazyStringSplitter splitter;
    
        // split at the characters ' ', '!', '.', ','
        splitter("This, is a string. And here is another string! Let's test and see how well this does.", " !.,");
    
        while (!splitter.empty())
            cout << splitter.next() << endl;
        return 0;
    }
     

    ВЫВОД

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

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

     vector<string> split_string(splitter.begin(), splitter.end());
     
    ответ дан smac89, с репутацией 11113, 27.12.2013
  • 2 рейтинг

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

     std::vector<size_t> str_pos(const std::string &search, const std::string &target)
    {
        std::vector<size_t> founds;
    
        if(!search.empty())
        {
            size_t start_pos = 0;
    
            while (true)
            {
                size_t found_pos = target.find(search, start_pos);
    
                if(found_pos != std::string::npos)
                {
                    size_t found = found_pos;
    
                    founds.push_back(found);
    
                    start_pos = (found_pos + 1);
                }
                else
                {
                    break;
                }
            }
        }
    
        return founds;
    }
    
    std::string str_sub_index(size_t begin_index, size_t end_index, const std::string &target)
    {
        std::string sub;
    
        size_t size = target.length();
    
        const char* copy = target.c_str();
    
        for(size_t i = begin_index; i <= end_index; i++)
        {
            if(i >= size)
            {
                break;
            }
            else
            {
                char c = copy[i];
    
                sub += c;
            }
        }
    
        return sub;
    }
    
    std::vector<std::string> str_split(const std::string &delimiter, const std::string &target)
    {
        std::vector<std::string> splits;
    
        if(!delimiter.empty())
        {
            std::vector<size_t> founds = str_pos(delimiter, target);
    
            size_t founds_size = founds.size();
    
            if(founds_size > 0)
            {
                size_t search_len = delimiter.length();
    
                size_t begin_index = 0;
    
                for(int i = 0; i <= founds_size; i++)
                {
                    std::string sub;
    
                    if(i != founds_size)
                    {
                        size_t pos  = founds.at(i);
    
                        sub = str_sub_index(begin_index, pos - 1, target);
    
                        begin_index = (pos + search_len);
                    }
                    else
                    {
                        sub = str_sub_index(begin_index, (target.length() - 1), target);
                    }
    
                    splits.push_back(sub);
                }
            }
        }
    
        return splits;
    }
     

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

    Протестирована функция в main() блоках следующим образом:

     int main()
    {
        std::string s = "Hello, world! We need to make the world a better place. Because your world is also my world, and our children's world.";
    
        std::vector<std::string> split = str_split("world", s);
    
        for(int i = 0; i < split.size(); i++)
        {
            std::cout << split[i] << std::endl;
        }
    }
     

    И это произвело бы:

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

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

    ответ дан yunhasnawa, с репутацией 445, 20.04.2016
  • 2 рейтинг

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

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

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

    ответ дан ManiP, с репутацией 419, 8.03.2012
  • 2 рейтинг

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

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

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

    ответ дан mchiasson, с репутацией 1251, 19.08.2014
  • 2 рейтинг

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

    Вот мой маленький алгоритм, используя только 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.

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

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

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

    ответ дан robcsi, с репутацией 175, 17.03.2014