Разделить строку с помощью С++ 11

Что будет проще всего разбить строку с помощью С++ 11?

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

Изменить: я хотел бы иметь vector<string> в результате и иметь возможность разграничивать один символ.

Ответ 1

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

std::vector<std::string> split(const string& input, const string& regex) {
    // passing -1 as the submatch index parameter performs splitting
    std::regex re(regex);
    std::sregex_token_iterator
        first{input.begin(), input.end(), re, -1},
        last;
    return {first, last};
}

Ответ 2

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

#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split(const std::string &s, char delim) {
  std::stringstream ss(s);
  std::string item;
  std::vector<std::string> elems;
  while (std::getline(ss, item, delim)) {
    elems.push_back(item);
    // elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
  }
  return elems;
}

Ответ 3

Здесь приведен пример разделения строки и заполнения вектора выделенными элементами с помощью boost.

#include <boost/algorithm/string.hpp>

std::string my_input("A,B,EE");
std::vector<std::string> results;

boost::algorithm::split(results, my_input, is_any_of(","));

assert(results[0] == "A");
assert(results[1] == "B");
assert(results[2] == "EE");

Ответ 4

Другое решение regex вдохновленное другими ответами, но, надеюсь, более короткое и удобное для чтения:

std::string s{"String to split here, and here, and here,..."};
std::regex regex{R"([\s,]+)"}; // split on space and comma
std::sregex_token_iterator it{s.begin(), s.end(), regex, -1};
std::vector<std::string> words{it, {}};

Ответ 5

Я не знаю, является ли это менее многословным, но может быть проще получить для тех более опытных в динамических языках, таких как javascript. Единственная функция С++ 11, которую он использует, - lambdas.

#include <algorithm>
#include <string>
#include <cctype>
#include <iostream>
#include <vector>

int main()
{
  using namespace std;
  string s = "hello  how    are you won't you tell me your name";
  vector<string> tokens;
  string token;

  for_each(s.begin(), s.end(), [&](char c) {
    if (!isspace(c))
        token += c;
    else 
    {
        if (token.length()) tokens.push_back(token);
        token.clear();
    }
  });
  if (token.length()) tokens.push_back(token);

  return 0;
}

Ответ 6

Мой выбор boost::tokenizer, но у меня не было никаких тяжелых задач и теста с огромными данными. Пример из boost doc с лямбда-модификацией:

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

int main()
{
   using namespace std;
   using namespace boost;

   string s = "This is,  a test";
   vector<string> v;
   tokenizer<> tok(s);
   for_each (tok.begin(), tok.end(), [&v](const string & s) { v.push_back(s); } );
   // result 4 items: 1)This 2)is 3)a 4)test
   return 0;
}

Ответ 7

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>


using namespace std;

vector<string> split(const string& str, int delimiter(int) = ::isspace){
  vector<string> result;
  auto e=str.end();
  auto i=str.begin();
  while(i!=e){
    i=find_if_not(i,e, delimiter);
    if(i==e) break;
    auto j=find_if(i,e, delimiter);
    result.push_back(string(i,j));
    i=j;
  }
  return result;
}

int main(){
  string line;
  getline(cin,line);
  vector<string> result = split(line);
  for(auto s: result){
    cout<<s<<endl;
  }
}

Ответ 8

Это мой ответ. Подробный, читаемый и эффективный.

std::vector<std::string> tokenize(const std::string& s, char c) {
    auto end = s.cend();
    auto start = end;

    std::vector<std::string> v;
    for( auto it = s.cbegin(); it != end; ++it ) {
        if( *it != c ) {
            if( start == end )
                start = it;
            continue;
        }
        if( start != end ) {
            v.emplace_back(start, it);
            start = end;
        }
    }
    if( start != end )
        v.emplace_back(start, end);
    return v;
}

Ответ 9

Вот решение С++ 11, которое использует только std :: string :: find(). Разделитель может содержать любое количество символов. Проанализированные токены выводятся через выходной итератор, который в моем коде обычно является std :: back_inserter.

Я не проверял это с UTF-8, но я ожидаю, что он должен работать, пока вход и разделитель являются допустимыми строками UTF-8.

#include <string>

template<class Iter>
Iter splitStrings(const std::string &s, const std::string &delim, Iter out)
{
    if (delim.empty()) {
        *out++ = s;
        return out;
    }
    size_t a = 0, b = s.find(delim);
    for ( ; b != std::string::npos;
          a = b + delim.length(), b = s.find(delim, a))
    {
        *out++ = std::move(s.substr(a, b - a));
    }
    *out++ = std::move(s.substr(a, s.length() - a));
    return out;
}

Некоторые тестовые случаи:

void test()
{
    std::vector<std::string> out;
    size_t counter;

    std::cout << "Empty input:" << std::endl;        
    out.clear();
    splitStrings("", ",", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }

    std::cout << "Non-empty input, empty delimiter:" << std::endl;        
    out.clear();
    splitStrings("Hello, world!", "", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }

    std::cout << "Non-empty input, non-empty delimiter"
                 ", no delimiter in string:" << std::endl;        
    out.clear();
    splitStrings("abxycdxyxydefxya", "xyz", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }

    std::cout << "Non-empty input, non-empty delimiter"
                 ", delimiter exists string:" << std::endl;        
    out.clear();
    splitStrings("abxycdxy!!xydefxya", "xy", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }

    std::cout << "Non-empty input, non-empty delimiter"
                 ", delimiter exists string"
                 ", input contains blank token:" << std::endl;        
    out.clear();
    splitStrings("abxycdxyxydefxya", "xy", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }

    std::cout << "Non-empty input, non-empty delimiter"
                 ", delimiter exists string"
                 ", nothing after last delimiter:" << std::endl;        
    out.clear();
    splitStrings("abxycdxyxydefxy", "xy", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }

    std::cout << "Non-empty input, non-empty delimiter"
                 ", only delimiter exists string:" << std::endl;        
    out.clear();
    splitStrings("xy", "xy", std::back_inserter(out));
    counter = 0;        
    for (auto i = out.begin(); i != out.end(); ++i, ++counter) {
        std::cout << counter << ": " << *i << std::endl;
    }
}

Ожидаемый результат:

Empty input:
0: 
Non-empty input, empty delimiter:
0: Hello, world!
Non-empty input, non-empty delimiter, no delimiter in string:
0: abxycdxyxydefxya
Non-empty input, non-empty delimiter, delimiter exists string:
0: ab
1: cd
2: !!
3: def
4: a
Non-empty input, non-empty delimiter, delimiter exists string, input contains blank token:
0: ab
1: cd
2: 
3: def
4: a
Non-empty input, non-empty delimiter, delimiter exists string, nothing after last delimiter:
0: ab
1: cd
2: 
3: def
4: 
Non-empty input, non-empty delimiter, only delimiter exists string:
0: 
1: 

Ответ 10

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

inline vector<string> split(const string& s) {
    vector<string> result;
    istringstream iss(s);
    for (string w; iss >> w; )
        result.push_back(w);
    return result;
}

Ответ 11

Один из возможных способов сделать это - найти все вхождения разделенной строки и сохранить местоположения в списке. Затем подсчитайте символы входной строки, и когда вы доберетесь до позиции, в которой есть "поисковый запрос" в списке позиций, вы перейдете на "длину разделенной строки". Этот подход принимает разделенную строку любой длины. Вот мое проверенное и рабочее решение.

#include <iostream>
#include <string>
#include <list>
#include <vector>

using namespace std;

vector<string> Split(string input_string, string search_string)
{
    list<int> search_hit_list;
    vector<string> word_list;
    size_t search_position, search_start = 0;

    // Find start positions of every substring occurence and store positions to a hit list.
    while ( (search_position = input_string.find(search_string, search_start) ) != string::npos) {
        search_hit_list.push_back(search_position);
        search_start = search_position + search_string.size();
    }

    // Iterate through hit list and reconstruct substring start and length positions
    int character_counter = 0;
    int start, length;

    for (auto hit_position : search_hit_list) {

        // Skip over substrings we are splitting with. This also skips over repeating substrings.
        if (character_counter == hit_position) {
            character_counter = character_counter + search_string.size();
            continue;
        }

        start = character_counter;
        character_counter = hit_position;
        length = character_counter - start;
        word_list.push_back(input_string.substr(start, length));
        character_counter = character_counter + search_string.size();
    }

    // If the search string is not found in the input string, then return the whole input_string.
    if (word_list.size() == 0) {
            word_list.push_back(input_string);
            return word_list;
    }
    // The last substring might be still be unprocessed, get it.
    if (character_counter < input_string.size()) {
        word_list.push_back(input_string.substr(character_counter, input_string.size() - character_counter));
    }

    return word_list;
}

int main() {

    vector<string> word_list;
    string search_string = " ";
    // search_string = "the";
    string text = "thetheThis is  some   text     to test  with the    split-thethe   function.";

    word_list = Split(text, search_string);

    for (auto item : word_list) {
        cout << "'" << item << "'" << endl;
    }

    cout << endl;
}