Hoe herhaal ik de woorden van een string?

Ik probeer de woorden van een tekenreeks te herhalen.

Er kan van worden uitgegaan dat de tekenreeks bestaat uit woorden gescheiden door witruimte.

Merk op dat ik niet geïnteresseerd ben in C-stringfuncties of dat soort karaktermanipulatie/toegang. Geef in uw antwoord ook voorrang aan elegantie boven efficiëntie.

De beste oplossing die ik nu heb is:

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

Is er een elegantere manier om dit te doen?


Antwoord 1, autoriteit 100%

Voor wat het waard is, hier is nog een manier om tokens uit een invoerreeks te extraheren, waarbij u alleen vertrouwt op standaardbibliotheekfaciliteiten. Het is een voorbeeld van de kracht en elegantie achter het ontwerp van de 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"));
}

In plaats van de geëxtraheerde tokens naar een uitvoerstroom te kopiëren, zou men ze in een container kunnen invoegen met dezelfde generieke copyalgoritme.

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

… of maak de vectordirect aan:

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};

Antwoord 2, autoriteit 99%

Ik gebruik dit om een string te splitsen door een scheidingsteken. De eerste plaatst de resultaten in een vooraf geconstrueerde vector, de tweede retourneert een nieuwe vector.

#include <string>
#include <sstream>
#include <vector>
#include <iterator>
template <typename Out>
void split(const std::string &s, char delim, Out result) {
    std::istringstream iss(s);
    std::string item;
    while (std::getline(iss, 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;
}

Merk op dat deze oplossing geen lege tokens overslaat, dus het volgende zal 4 items vinden, waarvan er één leeg is:

std::vector<std::string> x = split("one:two::three", ':');

Antwoord 3, autoriteit 59%

Een mogelijke oplossing voor het gebruik van Boost kan zijn:

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

Deze benadering is mogelijk zelfs sneller dan de stringstream-benadering. En aangezien dit een generieke sjabloonfunctie is, kan deze worden gebruikt om andere soorten tekenreeksen (wchar, enz. of UTF-8) te splitsen met behulp van allerlei soorten scheidingstekens.

Zie de documentatievoor details.


Antwoord 4, autoriteit 27%

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

Antwoord 5, autoriteit 13%

Voor degenen met wie het niet goed zit om alle efficiëntie op te offeren voor codegrootte en “efficiënt” te zien als een soort elegantie, zou het volgende een goede plek moeten raken (en ik denk dat de sjablooncontainerklasse een geweldig elegante toevoeging is .):

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

Ik kies er meestal voor om std::vector<std::string>typen te gebruiken als mijn tweede parameter (ContainerT)… maar list<>is veel sneller dan vector<>voor wanneer directe toegang niet nodig is, en je kunt zelfs je eigen tekenreeksklasse maken en zoiets gebruiken als std::list<subString>waarbij subStringgeen kopieën maakt voor ongelooflijke snelheidsverhogingen.

Het is meer dan twee keer zo snel als de snelste tokenize op deze pagina en bijna 5 keer sneller dan sommige andere. Ook met de perfecte parametertypes kun je alle string- en lijstkopieën elimineren voor extra snelheidsverhogingen.

Bovendien voert het niet de (extreem inefficiënte) resultaatteruggave uit, maar geeft het eerder de tokens door als referentie, waardoor u ook tokens kunt opbouwen met meerdere aanroepen als u dat wenst.

Ten slotte kunt u via een laatste optionele parameter specificeren of lege tokens uit de resultaten moeten worden verwijderd.

Alles wat het nodig heeft is std::string… de rest is optioneel. Het maakt geen gebruik van streams of de boost-bibliotheek, maar is flexibel genoeg om sommige van deze vreemde typen op natuurlijke wijze te kunnen accepteren.


Antwoord 6, autoriteit 12%

Hier is nog een oplossing. Het is compact en redelijk efficiënt:

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

Het kan gemakkelijk worden getempleerd om tekenreeksscheidingstekens, brede tekenreeksen, enz. te verwerken.

Merk op dat het splitsen van ""resulteert in een enkele lege tekenreeks en het splitsen van ","(dwz sep) resulteert in twee lege tekenreeksen.

Het kan ook eenvoudig worden uitgebreid om lege tokens over te slaan:

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

Als het splitsen van een string op meerdere scheidingstekens en het overslaan van lege tokens gewenst is, kan deze versie worden gebruikt:

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

Antwoord 7, autoriteit 9%

Dit is mijn favoriete manier om een string te doorlopen. Je kunt per woord doen wat je wilt.

string line = "a line of text to iterate through";
string word;
istringstream iss(line, istringstream::in);
while( iss >> word )     
{
    // Do something on `word` here...
}

Antwoord 8, autoriteit 6%

Dit is vergelijkbaar met Stack Overflow-vraag Hoe Ik tokenize een string in 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;
    }
}

Antwoord 9, autoriteit 5%

Ik vind het volgende leuk omdat het de resultaten in een vector plaatst, een string als scheidingsteken ondersteunt en controle geeft over het behouden van lege waarden. Maar dan ziet het er niet zo goed uit.

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

Natuurlijk heeft Boost een split()dat gedeeltelijk zo werkt. En als je met ‘witruimte’ echt elk type witruimte bedoelt, werkt het gebruik van Boost’s split met is_any_of()prima.


Antwoord 10, autoriteit 4%

De STL heeft een dergelijke methode nog niet beschikbaar.

U kunt echter ofwel C’s strtok()functie met behulp van de std::string::c_str()lid, of je kunt er zelf een schrijven. Hier is een codevoorbeeld dat ik vond na een snelle Google-zoekopdracht (“STL-tekenreekssplitsing”):

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

Genomen van: http://oopweb. com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

Als je vragen hebt over het codevoorbeeld, laat dan een reactie achter en ik zal het uitleggen.

En alleen omdat het geen typedefgenaamd iterator implementeert of de operator <<niet overbelast, betekent niet dat het slechte code is. Ik gebruik C-functies vrij vaak. Bijvoorbeeld printfen scanfbeide zijn sneller dan std::cinen std::cout(aanzienlijk), de fopensyntaxis is een stuk vriendelijker voor binaire typen, en ze hebben ook de neiging om kleinere EXE’s te produceren.

Laat u niet verleiden door deze ‘Elegantie boven prestatie’-deal.


Antwoord 11, autoriteit 3%

Hier is een splitsingsfunctie die:

  • is algemeen
  • gebruikt standaard C++ (geen boost)
  • accepteert meerdere scheidingstekens
  • neegt lege tokens (kan gemakkelijk worden gewijzigd)

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

Voorbeeld van gebruik:

   vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");

Antwoord 12, autoriteit 3%

Ik heb een oplossing van 2 regels voor dit probleem:

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;

In plaats van af te drukken, kunt u het in een vector plaatsen.


Antwoord 13, autoriteit 2%

Nog een flexibele en snelle manier

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

Om het te gebruiken met een vector van strings (Bewerken: aangezien iemand erop wees dat STL-klassen niet mogen erven… 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");

Dat is het! En dat is slechts één manier om het tokenizer te gebruiken, zoals gewoon
Telwoorden:

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

beperkt door de verbeelding;)


Antwoord 14, Autoriteit 2%

Hier is een eenvoudige oplossing die alleen de standaard Regex-bibliotheek

gebruikt

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

Het REGEX-argument maakt het mogelijk om te controleren op meerdere argumenten (spaties, komma’s, enz.)

Ik controleer meestal alleen op spaties en komma’s, dus ik heb ook deze standaardfunctie:

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

De "[\\s,]+"Controles voor spaties (\\s) EN KOMEN (,).

Let op, als je wstringwilt splitsen in plaats van string,

  • verander alle std::regexin std::wregex
  • verander alle sregex_token_iteratorin wsregex_token_iterator

Let op, misschien wil je het stringargument ook als referentie nemen, afhankelijk van je compiler.


Antwoord 15, autoriteit 2%

Het gebruik van std::stringstreamzoals je hebt gedaan werkt prima en doet precies wat je wilde. Als je echter gewoon op zoek bent naar een andere manier om dingen te doen, kun je std::find()/std::find_first_of()en std::string::substr().

Hier is een voorbeeld:

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

Antwoord 16, autoriteit 2%

Als je boost wilt gebruiken, maar een hele tekenreeks als scheidingsteken wilt gebruiken (in plaats van enkele tekens zoals in de meeste eerder voorgestelde oplossingen), kun je de boost_split_iteratorgebruiken.

Voorbeeldcode inclusief handig sjabloon:

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

Antwoord 17, autoriteit 2%

Er is een functie met de naam 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;
}

Antwoord 18

Hier is een regex-oplossing die alleen de standaard regex-bibliotheek gebruikt. (Ik ben een beetje roestig, dus er kunnen een paar syntaxisfouten zijn, maar dit is in ieder geval het algemene idee)

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

Antwoord 19

De Stringsstream kan handig zijn als u de string moet ontleden door niet- Ruimtesymbolen:

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

Antwoord 20

Tot nu toe gebruikte ik de in boost , maar ik had iets nodig dat niet ‘t hangt ervan af, dus ik kwam hierbij:

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

Een goed punt is dat je in separatorsmeer dan één teken kunt doorgeven.


Antwoord 21

Kort en elegant

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

kan elke string als scheidingsteken gebruiken, kan ook worden gebruikt met binaire data (std::string ondersteunt binaire data, inclusief nulls)

met:

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

uitvoer:

this
is
!example!string

Antwoord 22

Gebruik std::string_viewen Eric Niebler’s range-v3bibliotheek:

https://wandbox.org/permlink/kW5lwRCL1pxjp2pW

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"
int main() {
    std::string s = "Somewhere down the range v3 library";
    ranges::for_each(s  
        |   ranges::view::split(' ')
        |   ranges::view::transform([](auto &&sub) {
                return std::string_view(&*sub.begin(), ranges::distance(sub));
            }),
        [](auto s) {std::cout << "Substring: " << s << "\n";}
    );
}

Door een range forlus te gebruiken in plaats van ranges::for_eachalgoritme:

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
int main()
{
    std::string str = "Somewhere down the range v3 library";
    for (auto s : str | ranges::view::split(' ')
                      | ranges::view::transform([](auto&& sub) { return std::string_view(&*sub.begin(), ranges::distance(sub)); }
                      ))
    {
        std::cout << "Substring: " << s << "\n";
    }
}

Antwoord 23

Ik heb mijn eigen gegooid met strtok en boost gebruikt om een string te splitsen. De beste methode die ik heb gevonden is de C++ String Toolkit-bibliotheek. Het is ongelooflijk flexibel en snel.

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

De toolkit heeft veel meer flexibiliteit dan dit eenvoudige voorbeeld laat zien, maar het nut ervan bij het ontleden van een string in bruikbare elementen is ongelooflijk.


Antwoord 24

Ik heb dit gemaakt omdat ik een gemakkelijke manier nodig had om strings en op c gebaseerde strings te splitsen… Hopelijk kan iemand anders het ook nuttig vinden. Het is ook niet afhankelijk van tokens en je kunt velden als scheidingstekens gebruiken, wat een andere sleutel is die ik nodig had.

Ik weet zeker dat er verbeteringen kunnen worden aangebracht om de elegantie nog verder te verbeteren, en doe dat vooral

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

Voorbeelden:

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

Zal uitvoeren:

Dit
is
een
voorbeeld
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;
}

Om lege invoer te behouden (standaard worden lege gegevens uitgesloten):

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

Het doel was om het vergelijkbaar te maken met de Split()-methode van C#, waarbij het splitsen van een string net zo eenvoudig is als:

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

Ik hoop dat iemand anders dit net zo nuttig kan vinden als ik.


Antwoord 25

C++20 zegent ons eindelijk met een split-functie. Of beter gezegd, een bereikadapter. Godbolt-link.

#include <iostream>
#include <ranges>
#include <string_view>
namespace ranges = std::ranges;
namespace views = std::views;
using str = std::string_view;
constexpr auto view =
    "Multiple words"
    | views::split(' ')
    | views::transform([](auto &&r) -> str {
        return {
            &*r.begin(),
            static_cast<str::size_type>(ranges::distance(r))
        };
    });
auto main() -> int {
    for (str &&sv : view) {
        std::cout << sv << '\n';
    }
}

Antwoord 26

Dit antwoord neemt de string en plaatst deze in een vector van strings. Het gebruikt de boost-bibliotheek.

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

Antwoord 27

En dit:

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

Antwoord 28

Hier is een andere manier om het te doen..

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

Antwoord 29

Ik gebruik graag de boost/regex-methoden voor deze taak, omdat ze maximale flexibiliteit bieden voor het specificeren van de splitsingscriteria.

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

Antwoord 30

Onlangs moest ik een woord in een kameel opsplitsen in subwoorden. Er zijn geen scheidingstekens, alleen bovenste tekens.

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

Dit splitst bijvoorbeeld “AQueryTrades” in “A”, “Query” en “Trades”. De functie werkt met smalle en brede snaren. Omdat het de huidige locale respecteert, splitst het “RaumfahrtÜberwachungsVerordnung” in “Raumfahrt”, “Überwachungs” en “Verordnung”.

Opmerking std::uppermoet echt worden doorgegeven als functiesjabloonargument. Dan kan de meer algemene functie van deze functie zich splitsen bij scheidingstekens zoals ",", ";"of " "ook.

Other episodes