Een string splitsen met C++11

Wat zou de gemakkelijkste methode zijn om een string te splitsen met c++11?

Ik heb de methode gezien die wordt gebruikt door deze post, maar ik vind dat er een minder uitgebreide manier zou moeten zijn om het te doen met behulp van de nieuwe standaard.

Bewerken: ik zou graag een vector<string>als resultaat willen hebben en in staat zijn om een enkel teken af te bakenen.


Antwoord 1, autoriteit 100%

std::regex_token_iteratorvoert generieke tokenisatie uit op basis van een regex. Het kan al dan niet overkill zijn voor het eenvoudig splitsen van een enkel teken, maar het werkt en is niet te uitgebreid:

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

Antwoord 2, autoriteit 50%

Hier is een (misschien minder uitgebreide) manier om strings te splitsen (gebaseerd op de postdie je noemde).

p>

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

Antwoord 3, autoriteit 24%

Hier is een voorbeeld van het splitsen van een string en het vullen van een vector met de geëxtraheerde elementen met behulp van boost.

#include <boost/algorithm/string.hpp>
std::string my_input("A,B,EE");
std::vector<std::string> results;
boost::algorithm::split(results, my_input, boost::is_any_of(","));
assert(results[0] == "A");
assert(results[1] == "B");
assert(results[2] == "EE");

Antwoord 4, autoriteit 20%

Een andere regex-oplossing geïnspireerd door andere antwoordenmaar hopelijk korter en gemakkelijker te lezen:

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

Antwoord 5, autoriteit 8%

Ik weet niet of dit minder uitgebreid is, maar het is misschien makkelijker om te grokken voor mensen die meer ervaren zijn in dynamische talen zoals javascript. De enige C++11-functie die het gebruikt, is lambdas.

#include <string>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
  string s = "hello  how    are you won't you tell me your name";
  vector<string> tokens;
  string token;
  for (const auto& c: s) {
    if (!isspace(c))
      token += c;
    else {
      if (token.length()) tokens.push_back(token);
      token.clear();
    }
  }
  if (token.length()) tokens.push_back(token);
  return 0;
}

Antwoord 6, autoriteit 6%

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

Antwoord 7, Autoriteit 6%

Mijn keuze is boost::tokenizer, maar ik heb geen zware taken en test met enorme gegevens.
Voorbeeld uit boost document met lambda modificatie:

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

Antwoord 8, Autoriteit 5%

Dit is mijn antwoord. Verbose, leesbaar en efficiënt.

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

Antwoord 9, autoriteit 3%

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

Antwoord 10, autoriteit 2%

Hier is een C++11-oplossing die alleen std::string::find() gebruikt. Het scheidingsteken kan een willekeurig aantal tekens lang zijn. Geparseerde tokens worden uitgevoerd via een uitvoeriterator, wat meestal een std::back_inserter is in mijn code.

Ik heb dit niet getest met UTF-8, maar ik verwacht dat het zou moeten werken zolang de invoer en het scheidingsteken beide geldige UTF-8-tekenreeksen zijn.

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

Enkele testgevallen:

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

Verwachte uitvoer:

Lege invoer:
0:
Niet-lege input, lege scheidingsteken:
0: Hallo, World!
Niet-lege input, niet-lege scheidingsteken, geen scheidingsteken in tekenreeks:
0: abxycdxyxydefxya
Niet-lege input, niet-lege scheidingsteken, scheidingsteken bestaat uit een string:
0: ab
1: CD
2: !!
3: DEF
4: a
Niet-lege input, niet-lege scheidingsteken, scheidingsteken bestaat uit een string, invoer bevat leeg token:
0: ab
1: CD
2:
3: DEF
4: A
Niet-lege input, niet-lege scheidingsteken, scheidingsteken bestaat uit een string, niets na laatste scheidingsteken:
0: ab
1: CD
2:
3: DEF
4:
Niet-lege input, niet-lege scheidingsteken, alleen scheidingsteken bestaat uit een string:
0:
1:

Antwoord 11

Een mogelijke manier om dit te doen is het vinden van alle voorvallen van de gesplitste string en het opslaan van locaties op een lijst. Tel vervolgens ingangstekenarakters en wanneer u op een positie komt waar er een ‘zoekhit’ in de positielijst is, springt u naar voren met de ‘lengte van de gesplitste string’. Deze aanpak neemt een gesplitste reeks van elke lengte. Hier is mijn geteste en werkoplossing.

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

Other episodes