it-swarm.com.de

Wie kann ich die Wörter einer Zeichenfolge durchlaufen?

Ich versuche, die Wörter einer Zeichenfolge zu durchlaufen.

Es kann angenommen werden, dass die Zeichenfolge aus Wörtern besteht, die durch Leerzeichen getrennt sind.

Beachten Sie, dass ich nicht an C-String-Funktionen oder dieser Art von Zeichenmanipulation/Zugriff interessiert bin. Bitte geben Sie in Ihrer Antwort der Eleganz Vorrang vor Effizienz.

Die beste Lösung, die ich jetzt habe, ist:

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

Gibt es einen eleganteren Weg, dies zu tun?

2767
Ashwin Nanjappa

Für das, was es wert ist, gibt es eine weitere Möglichkeit, Token aus einer Eingabezeichenfolge zu extrahieren, wobei nur Standard-Bibliotheksfunktionen verwendet werden. Es ist ein Beispiel für die Kraft und Eleganz, die hinter dem Design des STL steht.

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

Anstatt die extrahierten Token in einen Ausgabestrom zu kopieren, könnten sie mit demselben generischen copy-Algorithmus in einen Container eingefügt werden.

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

... oder erstellen Sie die vector direkt:

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

Ich benutze dies, um einen String durch ein Trennzeichen zu trennen. Der erste setzt die Ergebnisse in einen vorkonstruierten Vektor, der zweite gibt einen neuen Vektor zurück.

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

Beachten Sie, dass diese Lösung keine leeren Token überspringt. Daher werden im Folgenden 4 Elemente gefunden, von denen einer leer ist:

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

Eine mögliche Lösung mit Boost könnte sein:

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

Dieser Ansatz ist möglicherweise noch schneller als der Ansatz stringstream. Da es sich hierbei um eine generische Template-Funktion handelt, können andere Stringtypen (wchar usw. oder UTF-8) mit allen Arten von Trennzeichen aufgeteilt werden.

Einzelheiten finden Sie in der Dokumentation documentation .

813
ididak
#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;
}
342
kev

Für diejenigen, mit denen es nicht gut geht, die gesamte Effizienz für die Codegröße zu opfern und "effizient" als eine Art von Eleganz zu betrachten, sollten die folgenden Punkte einen guten Punkt treffen (und ich denke, dass die Klasse der Vorlagencontainer eine äußerst elegante Ergänzung ist.):

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

Normalerweise wähle ich std::vector<std::string>-Typen als zweiten Parameter (ContainerT) ..., aber list<> ist viel schneller als vector<>, wenn kein direkter Zugriff erforderlich ist, und Sie können sogar eine eigene String-Klasse erstellen und etwas wie std::list<subString> verwenden, wobei subString nicht verwendet wird mache Kopien für unglaubliche Geschwindigkeitssteigerungen.

Es ist mehr als doppelt so schnell wie das schnellste Tokenize auf dieser Seite und fast fünfmal schneller als andere. Mit den perfekten Parametertypen können Sie auch alle String- und Listenkopien für zusätzliche Geschwindigkeitssteigerungen entfernen.

Außerdem wird keine (extrem ineffiziente) Rückgabe des Ergebnisses durchgeführt, sondern die Token werden als Referenz übergeben, sodass Sie Token auch mithilfe mehrerer Aufrufe aufbauen können, wenn Sie dies wünschen.

Zuletzt können Sie festlegen, ob leere Token mithilfe eines letzten optionalen Parameters aus den Ergebnissen entfernt werden sollen.

Alles was es braucht ist std::string... der Rest ist optional. Es verwendet keine Streams oder die Boost-Bibliothek, ist aber flexibel genug, um einige dieser Fremdtypen auf natürliche Weise akzeptieren zu können.

176
Marius

Hier ist eine andere Lösung. Es ist kompakt und ziemlich effizient:

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

Es kann leicht für Templatetrenner, breite Strings usw. verwendet werden.

Beachten Sie, dass das Aufteilen von "" eine einzelne leere Zeichenfolge und das Aufteilen von "," (dh. Sep) zwei leere Zeichenfolgen ergibt.

Es kann auch leicht erweitert werden, um leere Token zu überspringen:

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

Wenn Sie eine Zeichenfolge an mehreren Trennzeichen aufteilen und leere Token überspringen möchten, kann diese Version verwendet werden:

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;
}
156
Alec Thomas

Dies ist meine Lieblingsmethode, um durch eine Zeichenfolge zu iterieren. Sie können machen, was Sie wollen, pro Wort.

string line = "a line of text to iterate through";
string Word;

istringstream iss(line, istringstream::in);

while( iss >> Word )     
{
    // Do something on `Word` here...
}
113
gnomed

Dies ist vergleichbar mit der Stack Overflow-Frage Wie kann ich eine Zeichenfolge in C++ tokenieren?.

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

Ich mag Folgendes, weil es die Ergebnisse in einen Vektor einfügt, eine Zeichenkette als Trennzeichen unterstützt und die Kontrolle über das Beibehalten leerer Werte ermöglicht. Aber es sieht nicht so gut aus.

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

Natürlich hat Boost eine split() , die teilweise so funktioniert. Wenn Sie mit "Leerraum" wirklich jede Art von Leerraum meinen, ist die Verwendung von Boosts Split mit is_any_of() großartig.

66
Shadow2531

In der STL steht eine solche Methode noch nicht zur Verfügung.

Sie können jedoch entweder die C-Funktion strtok() verwenden, indem Sie das std::string::c_str() -Mitglied verwenden, oder Sie können Ihre eigene schreiben. Hier ist ein Codebeispiel, das ich nach einer schnellen Google-Suche gefunden habe ("STL string split"):

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

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

Wenn Sie Fragen zum Codebeispiel haben, hinterlassen Sie einen Kommentar, und ich erkläre es Ihnen.

Und nur weil es keine typedef namens Iterator oder Überladung implementiert, bedeutet der <<-Operator nicht, dass es sich um fehlerhaften Code handelt. Ich benutze häufig C-Funktionen. Zum Beispiel sind printf und scanf beide schneller als std::cin und std::cout (signifikant), die fopen -Syntax ist für binäre Typen und Sie neigen auch dazu, kleinere EXEs zu erzeugen.

Lassen Sie sich bei diesem "Elegance over Performance" -Angebot nicht verkaufen.

52
nlaq

Hier ist eine Split-Funktion, die:

  • ist generisch
  • verwendet Standard C++ (kein Boost)
  • akzeptiert mehrere Trennzeichen
  • ignoriert leere Token (kann leicht geändert werden)

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

Verwendungsbeispiel:

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

Ich habe eine 2-Zeilen-Lösung für dieses Problem:

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;

Anstatt zu drucken, können Sie es in einen Vektor setzen.

35
rhomu

Noch ein flexibler und schneller Weg

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

So verwenden Sie es mit einem Vektor von Zeichenfolgen (Bearbeiten: Da jemand darauf hingewiesen hat, STL-Klassen nicht zu erben ... 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");

Das ist es! Und das ist nur eine Möglichkeit, den Tokenizer zu verwenden, wie einfach Wörter zu zählen: Wörter zählen:

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

Begrenzt durch Vorstellungskraft;)

35
Robert

Hier ist eine einfache Lösung, die nur die Standard-Regex-Bibliothek verwendet

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

Das Regex-Argument ermöglicht die Prüfung auf mehrere Argumente (Leerzeichen, Kommas usw.).

Normalerweise prüfe ich nur, ob Leerzeichen und Kommas getrennt werden, daher habe ich auch diese Standardfunktion:

std::vector<string> TokenizeDefault( const string str )
{
    using namespace std;

    regex re( "[\\s,]+" );

    return Tokenize( str, re );
}

Der "[\\s,]+" sucht nach Leerzeichen (\\s) und Kommas (,).

Beachten Sie, wenn Sie wstring anstelle von string aufteilen möchten,

  • alle std::regex in std::wregex ändern
  • alle sregex_token_iterator in wsregex_token_iterator ändern

Beachten Sie, dass Sie das Zeichenfolgenargument je nach Compiler auch als Referenz verwenden möchten.

31
dk123

Wenn Sie boost verwenden möchten, aber einen ganzen String als Trennzeichen verwenden möchten (anstelle von Einzelzeichen wie in den meisten der zuvor vorgeschlagenen Lösungen), können Sie den boost_split_iterator verwenden.

Beispielcode mit praktischer Vorlage:

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

Die Verwendung von std::stringstream as funktioniert einwandfrei und macht genau das, was Sie wollten. Wenn Sie nur nach einer anderen Vorgehensweise suchen, können Sie std::find() / std::find_first_of() und std::string::substr() verwenden.

Hier ist ein Beispiel:

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

Es gibt eine Funktion namens 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;
}
19
Pratik Deoghare

Dies ist eine Regex-Lösung, die nur die Standard-Regex-Bibliothek verwendet. (Ich bin ein bisschen verrostet, daher kann es ein paar Syntaxfehler geben, aber das ist zumindest die allgemeine 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
}
18
AJMansfield

Der stringstream kann praktisch sein, wenn Sie den String nach Nicht-Leerzeichen-Symbolen analysieren müssen:

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

Bisher habe ich die in Boost verwendet, aber ich brauchte etwas, das nicht davon abhängt.

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

Ein guter Punkt ist, dass Sie in separators mehrere Zeichen übergeben können.

14
Goran

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

kann einen beliebigen String als Trennzeichen verwenden, kann auch mit binären Daten verwendet werden (std :: string unterstützt binäre Daten einschließlich Nullen)

mit:

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

ausgabe:

this
is
!example!string
14
user1438233

Ich habe meine eigene mit strtok gewürfelt und mit boost einen String geteilt. Die beste Methode, die ich gefunden habe, ist die C++ String Toolkit Library . Es ist unglaublich flexibel und schnell.

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

Das Toolkit bietet viel mehr Flexibilität als dieses einfache Beispiel zeigt, aber es ist unglaublich nützlich, eine Zeichenfolge in nützliche Elemente zu zerlegen.

13
DannyK

Ich habe das gemacht, weil ich einen einfachen Weg brauchte, um Strings und c-basierte Strings zu trennen. Es ist auch nicht auf Token angewiesen und Sie können Felder als Trennzeichen verwenden. Dies ist ein weiterer Schlüssel, den ich brauche.

Ich bin sicher, dass es Verbesserungen gibt, die gemacht werden können, um seine Eleganz noch weiter zu verbessern. Bitte tun Sie dies auf jeden Fall

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

Beispiele:

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

Wird ausgegeben:

Diese
ist
ein
Beispiel
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;
}

Leere Einträge lassen (Leergut wird standardmäßig ausgeschlossen):

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

Das Ziel war es, es der Split () - Methode von C # ähnlich zu machen, wobei das Aufteilen einer Zeichenfolge so einfach ist wie:

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

foreach(String X in Split)
{
    Console.Write(X);
}

Ich hoffe, jemand anderes kann das genauso nützlich finden wie ich.

11
Steve Dell

Was ist mit diesem:

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

Diese Antwort nimmt den String und setzt ihn in einen Vektor von Strings. Es verwendet die Boost-Bibliothek.

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

Ich benutze gerne die Boost/Regex-Methoden für diese Aufgabe, da sie maximale Flexibilität bei der Festlegung der Aufteilungskriterien bieten.

#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;
}
9
Marty B

Hier ist eine andere Möglichkeit, es zu tun ..

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

Vor kurzem musste ich ein Kamelhülle-Wort in Unterwörter zerlegen. Es gibt keine Trennzeichen, nur obere Zeichen. 

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

Beispielsweise werden "AQueryTrades" in "A", "Query" und "Trades" aufgeteilt. Die Funktion arbeitet mit schmalen und breiten Strings. Da es das aktuelle Gebiet beachtet, teilt es die RaumfahrtÜberwachungsVerordnung in "Raumfahrt", "Überwachungs" und "Verordnung".

Hinweis std::upper sollte wirklich als Funktionsvorlagenargument übergeben werden. Dann können auch die allgemeineren Funktionen dieser Funktion an Trennzeichen wie ",", ";" oder " " aufgeteilt werden.

9

Der folgende Code verwendet strtok(), um eine Zeichenfolge in Token aufzuteilen und die Token in einem Vektor zu speichern.

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

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

Dieses Beispiel gibt die Ausgabe - 

Somewhere
down
the
road
8
Aleksey Bykov

Ich benutze dieses Simpleton, weil wir unsere String-Klasse "special" (d. H. Nicht Standard) haben:

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);
}
7
Abe
#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, das ist das, was Pythons re.split () am nächsten kommt. Weitere Informationen zu regex_token_iterator finden Sie unter cplusplus.com . Das -1 (viertes Argument in regex_token_iterator ctor) ist der Abschnitt der Sequenz, der nicht übereinstimmt, wobei die Übereinstimmung als Trennzeichen verwendet wird.

6
solstice333

Das Folgende ist ein viel besserer Weg, dies zu tun. Es kann ein beliebiges Zeichen annehmen und teilt nur Linien, wenn Sie möchten. Keine speziellen Bibliotheken erforderlich (naja, außer std, aber wer hält das für eine zusätzliche Bibliothek), keine Zeiger, keine Referenzen und es ist statisch. Einfach nur einfaches 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;
        }
};
6
Kelly Elton

Als Hobbyist ist dies die erste Lösung, die mir in den Sinn kam. Ich bin irgendwie neugierig, warum ich hier noch keine ähnliche Lösung gesehen habe. Gibt es etwas Grundsätzliches an meiner Vorgehensweise?

#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

5
Jehjoa

Ich habe den folgenden Code geschrieben. Sie können Trennzeichen angeben, bei dem es sich um eine Zeichenfolge handeln kann ..__ Das Ergebnis ähnelt Javas String.split, wobei im Ergebnis eine leere Zeichenfolge enthalten ist.

Wenn wir beispielsweise split aufrufen ("ABCPICKABCANYABCTWO: ABC", "ABC"), lautet das Ergebnis wie folgt:

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

Code:

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;
}
5
Jim Huang

Wenn Sie Whitespace als Trennzeichen verwenden, wird die offensichtliche Antwort auf die Verwendung von std::istream_iterator<T> bereits gegeben und viel gewählt. Natürlich dürfen Elemente nicht durch Leerzeichen sondern durch ein Trennzeichen getrennt werden. Ich habe keine Antwort gefunden, die lediglich die Bedeutung von Leerzeichen als Trennzeichen definiert und dann den konventionellen Ansatz verwendet.

Um zu ändern, welche Streams Whitespace berücksichtigen, ändern Sie einfach den std::locale des Streams mit (std::istream::imbue()) mit einer std::ctype<char>-Facette mit einer eigenen Definition dessen, was Whitespace bedeutet (dies ist auch für std::ctype<wchar_t> möglich, aber es ist tatsächlich etwas anders std::ctype<char> ist tabellengesteuert, während std::ctype<wchar_t> von virtuellen Funktionen gesteuert wird.

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

Der größte Teil des Codes dient zum Zusammenfassen eines Universalwerkzeugs, das weiche Trennzeichen bereitstellt:/Mehrere Trennzeichen in einer Reihe werden zusammengeführt. Es gibt keine Möglichkeit, eine leere Sequenz zu erzeugen. Wenn innerhalb eines Streams unterschiedliche Trennzeichen benötigt werden, verwenden Sie wahrscheinlich Streams mit einem gemeinsam genutzten Stream-Puffer anders eingerichtet:

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
        // ...
    }
}
5
Dietmar Kühl

Hier ist meine Lösung mit C++ 11 und derSTL. Es sollte einigermaßen effizient sein:

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

Ausgabe:

this
is
the
string
to
split
4
Galik

Dies ist meine Version, die der Quelle von Kev entnommen ist:

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

Rufen Sie danach die Funktion auf und machen Sie etwas damit:

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

Ich verwende den folgenden Code:

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

Schnellversion, die vector als Basisklasse verwendet und allen Operatoren uneingeschränkten Zugriff gewährt

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

Beispiel zum Auffüllen eines STL-Sets:

std::set<std::string> words;
Split split("Hello,World", ",");
words.insert(split.begin(), split.end());
3
landen

Kein Boost, keine String-Streams, nur die Standard-C-Bibliothek, die mit std::string und std::list: C-Bibliotheksfunktionen zur einfachen Analyse zusammenarbeitet, C++ - Datentypen zur einfachen Speicherverwaltung.

Whitespace wird als Kombination von Zeilenumbrüchen, Registerkarten und Leerzeichen betrachtet. Die Menge der Leerzeichen wird durch die Variable wschars festgelegt.

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

Lauf:

$ ./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}

Schwanzrekursive Version von split (selbst in zwei Funktionen aufgeteilt). Alle destruktiven Manipulationen von Variablen sind weg, außer dass Strings in die Liste übernommen werden!

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

Ja, ich habe alle 30 Beispiele durchgesehen.

Ich konnte keine Version von split finden, die für Trennzeichen mit mehreren Zeichen geeignet ist. 

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

Wahrscheinlich nicht die effizienteste Implementierung, aber es ist eine sehr rekursive Lösung, die nur <string> und <vector> verwendet.

Ah, es ist in C++ 11 geschrieben, aber es gibt nichts Besonderes an diesem Code, so dass Sie ihn leicht an C++ 98 anpassen könnten.

2
Romário

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();
    }
};
  • Ich nenne diese Methode aus einem Grund LazyStringSplitter - Die Zeichenfolge wird nicht in einem Schritt geteilt.
  • Im Wesentlichen verhält es sich wie ein Python-Generator
  • Es macht eine Methode mit dem Namen next verfügbar, die die nächste vom Original getrennte Zeichenfolge zurückgibt
  • Ich habe die unordered_set from c ++ 11 STL verwendet, so dass das Auffinden von Trennzeichen um ein Vielfaches schneller ist
  • Und so funktioniert es

TESTPROGRAMM

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

AUSGABE

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

Der nächste Plan zur Verbesserung dieses Problems besteht darin, begin- und end-Methoden so zu implementieren, dass Folgendes ausgeführt werden kann:

vector<string> split_string(splitter.begin(), splitter.end());
2
smac89

Für diejenigen, die eine Alternative zum Teilen von Zeichenfolgen mit einem Zeichenfolgenbegrenzer benötigen, können Sie vielleicht meine folgende Lösung ausprobieren.

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

Diese Schnipsel bestehen aus 3 Funktionen. Die schlechte Nachricht ist die Verwendung der str_split-Funktion. Sie benötigen die beiden anderen Funktionen. Ja, es ist ein riesiger Code. Aber die gute Nachricht ist, dass diese zwei zusätzlichen Funktionen unabhängig voneinander arbeiten können und manchmal auch nützlich sein können. :)

Die Funktion im main()-Block wurde wie folgt getestet:

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

Und es würde produzieren:

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

Ich glaube, das ist nicht der effizienteste Code, aber zumindest funktioniert es. Ich hoffe es hilft.

2
yunhasnawa

Ich benutze das Folgende

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, ich habe vergessen, die zusätzliche Prüfung (curr> ts) für das Entfernen von Token mit Leerzeichen einzuschließen. 

2
ManiP

Dies ist meine Lösung für dieses Problem:

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

Diese Funktion gibt einen Vektor von Strings zurück.

2
pz64_

Hier ist meine Version

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

Es funktioniert mit Trennzeichen mit mehreren Zeichen. Es verhindert, dass leere Token in Ihre Ergebnisse gelangen. Es verwendet einen einzelnen Header. Es gibt die Zeichenfolge als ein einzelnes Token zurück, wenn Sie kein Trennzeichen angeben. Wenn die Zeichenfolge leer ist, wird auch ein leeres Ergebnis zurückgegeben. Es ist leider ineffizient wegen der großen std::vector copy, ES SEI DENNSie kompilieren mit C++ 11, das den Verschiebungsschema verwenden sollte. In C++ 11 sollte dieser Code schnell sein.

2
mchiasson

Ich habe nach einer Möglichkeit gesucht, eine Zeichenfolge durch ein Trennzeichen beliebiger Länge aufzuteilen, und begann mit dem Schreiben von Grund auf, da vorhandene Lösungen nicht zu mir passten.

Hier ist mein kleiner Algorithmus, der nur STL verwendet:

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

Es kann mit Trennzeichen beliebiger Länge und Form verwendet werden, soweit ich es getestet habe . Instanziieren Sie entweder mit dem String- oder dem Wstring-Typ.

Der Algorithmus sucht nur nach dem Trennzeichen, ruft den Teil des Strings ab, der dem Trennzeichen entspricht, löscht das Trennzeichen und sucht erneut, bis er es nicht mehr findet.

Natürlich können Sie für das Trennzeichen eine beliebige Anzahl von Leerzeichen verwenden.

Ich hoffe, es hilft.

2
robcsi

Diese Funktion habe ich geschrieben und hilft mir dabei, viel zu tun. Es hat mir geholfen, wenn ich das Protokoll für WebSockets mache. 

using namespace std;
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

vector<string> split ( string input , string split_id ) {
  vector<string> result;
  int i = 0;
  bool add;
  string temp;
  stringstream ss;
  size_t found;
  string real;
  int r = 0;
    while ( i != input.length() ) {
        add = false;
        ss << input.at(i);
        temp = ss.str();
        found = temp.find(split_id);
        if ( found != string::npos ) {
            add = true;
            real.append ( temp , 0 , found );
        } else if ( r > 0 &&  ( i+1 ) == input.length() ) {
            add = true;
            real.append ( temp , 0 , found );
        }
        if ( add ) {
            result.Push_back(real);
            ss.str(string());
            ss.clear();
            temp.clear();
            real.clear();
            r = 0;
        }
        i++;
        r++;
    }
  return result;
}

int main() {
    string s = "S,o,m,e,w,h,e,r,e, down the road \n In a really big C++ house.  \n  Lives a little old lady.   \n   That no one ever knew.    \n    She comes outside.     \n     In the very hot Sun.      \n\n\n\n\n\n\n\n   And throws C++ at us.    \n    The End.  FIN.";
    vector < string > Token;
    Token = split ( s , "," );
    for ( int i = 0 ; i < Token.size(); i++)    cout << Token.at(i) << endl;
    cout << endl << Token.size();
    int a;
    cin >> a;
    return a;
}
2
User

Hier ist mein Eintrag:

template <typename Container, typename InputIter, typename ForwardIter>
Container
split(InputIter first, InputIter last,
      ForwardIter s_first, ForwardIter s_last)
{
    Container output;

    while (true) {
        auto pos = std::find_first_of(first, last, s_first, s_last);
        output.emplace_back(first, pos);
        if (pos == last) {
            break;
        }

        first = ++pos;
    }

    return output;
}

template <typename Output = std::vector<std::string>,
          typename Input = std::string,
          typename Delims = std::string>
Output
split(const Input& input, const Delims& delims = " ")
{
    using std::cbegin;
    using std::cend;
    return split<Output>(cbegin(input), cend(input),
                         cbegin(delims), cend(delims));
}

auto vec = split("Mary had a little lamb");

Die erste Definition ist eine generische STL-Funktion, die zwei Iteratorenpaare verwendet. Die zweite ist eine Komfortfunktion, mit der Sie alle begin()s und end()s selbst erledigen müssen. Sie können den Ausgabecontainertyp auch als Vorlagenparameter angeben, wenn Sie beispielsweise eine list verwenden möchten.

Was es elegant macht (IMO) ist, dass es im Gegensatz zu den meisten anderen Antworten nicht auf Zeichenfolgen beschränkt ist, sondern mit jedem STL-kompatiblen Container funktioniert. Ohne Änderung des obigen Codes können Sie Folgendes sagen:

using vec_of_vecs_t = std::vector<std::vector<int>>;

std::vector<int> v{1, 2, 0, 3, 4, 5, 0, 7, 8, 0, 9};
auto r = split<vec_of_vecs_t>(v, std::initializer_list<int>{0, 2});

dadurch wird der Vektor v jedes Mal in separate Vektoren aufgeteilt, wenn ein 0 oder ein 2 gefunden wird.

(Außerdem gibt es den zusätzlichen Vorteil, dass diese Implementierung bei Strings schneller ist als die auf strtok()- und getline()- basierten Versionen, zumindest auf meinem System.)

1
Tristan Brindle

Nur zur Bequemlichkeit:

template<class V, typename T>
bool in(const V &v, const T &el) {
    return std::find(v.begin(), v.end(), el) != v.end();
}

Die tatsächliche Aufteilung basiert auf mehreren Trennzeichen:

std::vector<std::string> split(const std::string &s,
                               const std::vector<char> &delims) {
    std::vector<std::string> res;
    auto stuff = [&delims](char c) { return !in(delims, c); };
    auto space = [&delims](char c) { return in(delims, c); };
    auto first = std::find_if(s.begin(), s.end(), stuff);
    while (first != s.end()) {
        auto last = std::find_if(first, s.end(), space);
        res.Push_back(std::string(first, last));
        first = std::find_if(last + 1, s.end(), stuff);
    }
    return res;
}

Die Verwendung:

int main() {
    std::string s = "   aaa,  bb  cc ";
    for (auto el: split(s, {' ', ','}))
        std::cout << el << std::endl;
    return 0;
}
1
AlwaysLearning
// adapted from a "regular" csv parse
std::string stringIn = "my csv  is 10233478 NOTseparated by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
    if (stringIn[i] == " ") {
        commaSeparated.Push_back("");
        commaCounter++;
    } else {
        commaSeparated.at(commaCounter) += stringIn[i];
    }
}

am Ende haben Sie einen Vektor von Strings, bei dem jedes Element im Satz durch Leerzeichen getrennt ist. Nur eine nicht standardmäßige Ressource ist std :: vector (da es sich jedoch um einen std :: string handelt, dachte ich, es wäre akzeptabel).

leere Zeichenfolgen werden als separate Elemente gespeichert.

1
tony gil

Ich glaube, niemand hat diese Lösung bisher veröffentlicht. Anstatt Trennzeichen direkt zu verwenden, macht es im Grunde dasselbe wie boost :: split (), d. H. Sie können ein Prädikat übergeben, das true zurückgibt, wenn ein Zeichen ein Trennzeichen ist, andernfalls false. Ich denke, das gibt dem Programmierer viel mehr Kontrolle und das Tolle ist, dass Sie keinen Boost brauchen.

template <class Container, class String, class Predicate>
void split(Container& output, const String& input,
           const Predicate& pred, bool trimEmpty = false) {
    auto it = begin(input);
    auto itLast = it;
    while (it = find_if(it, end(input), pred), it != end(input)) {
        if (not (trimEmpty and it == itLast)) {
            output.emplace_back(itLast, it);
        }
        ++it;
        itLast = it;
    }
}

Dann kannst du es so benutzen:

struct Delim {
    bool operator()(char c) {
        return not isalpha(c);
    }
};    

int main() {
    string s("#include<iostream>\n"
             "int main() { std::cout << \"Hello world!\" << std::endl; }");

    vector<string> v;

    split(v, s, Delim(), true);
    /* Which is also the same as */
    split(v, s, [](char c) { return not isalpha(c); }, true);

    for (const auto& i : v) {
        cout << i << endl;
    }
}
1
LLLL

Mein Code lautet:

#include <list>
#include <string>
template<class StringType = std::string, class ContainerType = std::list<StringType> >
class DSplitString:public ContainerType
{
public:
    explicit DSplitString(const StringType& strString, char cChar, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(cChar, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                Push_back(strTemp);
            iPos = iPos_char + 1;
        }
    }
    explicit DSplitString(const StringType& strString, const StringType& strSub, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(strSub, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                Push_back(strTemp);
            iPos = iPos_char + strSub.length();
        }
    }
};

Beispiel:

#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
    DSplitString<> aa("doicanhden1;doicanhden2;doicanhden3;", ';');
    for each (std::string var in aa)
    {
        std::cout << var << std::endl;
    }
    std::cin.get();
    return 0;
}
1
doicanhden

Wir können strtok in c ++ verwenden,

#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;
}
1
Venkata Naidu M
#include <iostream>
#include <vector>
using namespace std;

int main() {
  string str = "ABC AABCD CDDD RABC GHTTYU FR";
  str += " "; //dirty hack: adding extra space to the end
  vector<string> v;

  for (int i=0; i<(int)str.size(); i++) {
    int a, b;
    a = i;

    for (int j=i; j<(int)str.size(); j++) {
      if (str[j] == ' ') {
        b = j;
        i = j;
        break;
      }
    }
    v.Push_back(str.substr(a, b-a));
  }

  for (int i=0; i<v.size(); i++) {
    cout<<v[i].size()<<" "<<v[i]<<endl;
  }
  return 0;
}
1
torayeff

Schleife über getline mit '' als Token.

1
lemic

Hier ist meine Meinung dazu. Ich musste die Eingabezeichenfolge Word für Word verarbeiten, was durch die Verwendung von Leerzeichen zum Zählen von Wörtern möglich gewesen wäre. Ich hatte jedoch das Gefühl, dass dies langweilig werden würde, und ich sollte die Wörter in Vektoren aufteilen. 

#include<iostream>
#include<vector>
#include<string>
#include<stdio.h>
using namespace std;
int main()
{
    char x = '\0';
    string s = "";
    vector<string> q;
    x = getchar();
    while(x != '\n')
    {
        if(x == ' ')
        {
            q.Push_back(s);
            s = "";
            x = getchar();
            continue;
        }
        s = s + x;
        x = getchar();
    }
    q.Push_back(s);
    for(int i = 0; i<q.size(); i++)
        cout<<q[i]<<" ";
    return 0;
}
  1. Kümmert sich nicht um mehrere Leerzeichen.
  2. Wenn auf das letzte Wort nicht unmittelbar ein Zeilenvorschub folgt, wird der Leerraum zwischen dem letzten Zeichen des letzten Wortes und dem Zeilenvorschubzeichen eingefügt.
1
Saksham Sharma

Ich habe gerade ein gutes Beispiel dafür geschrieben, wie man ein Zeichen nach Symbolen aufteilt, das dann jedes Array von Zeichen (durch Ihr Symbol getrennte Wörter) in einen Vektor setzt. Zur Vereinfachung habe ich den Vektortyp der Standard-Zeichenfolge erstellt.

Ich hoffe das hilft und ist für Sie lesbar.

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

void Push(std::vector<std::string> &WORDS, std::string &TMP){
    WORDS.Push_back(TMP);
    TMP = "";
}
std::vector<std::string> mySplit(char STRING[]){
        std::vector<std::string> words;
        std::string s;
        for(unsigned short i = 0; i < strlen(STRING); i++){
            if(STRING[i] != ' '){
                s += STRING[i];
            }else{
                Push(words, s);
            }
        }
        Push(words, s);//Used to get last split
        return words;
}

int main(){
    char string[] = "My awesome string.";
    std::cout << mySplit(string)[2];
    std::cin.get();
    return 0;
}
1
user2588062

Basierend auf Galiks Antwort habe ich das gemacht. Das ist meistens hier, also muss ich es nicht immer wieder schreiben. Es ist verrückt, dass C++ noch keine native Split-Funktion hat. Eigenschaften:

  • Sollte sehr schnell sein.
  • Leicht zu verstehen (denke ich).
  • Führt leere Abschnitte zusammen.
  • Trivial zur Verwendung mehrerer Trennzeichen (z. B. "\r\n")
#include <string>
#include <vector>
#include <algorithm>

std::vector<std::string> split(const std::string& s, const std::string& delims)
{
    using namespace std;

    vector<string> v;

    // Start of an element.
    size_t elemStart = 0;

    // We start searching from the end of the previous element, which
    // initially is the start of the string.
    size_t elemEnd = 0;

    // Find the first non-delim, i.e. the start of an element, after the end of the previous element.
    while((elemStart = s.find_first_not_of(delims, elemEnd)) != string::npos)
    {
        // Find the first delem, i.e. the end of the element (or if this fails it is the end of the string).
        elemEnd = s.find_first_of(delims, elemStart);
        // Add it.
        v.emplace_back(s, elemStart, elemEnd == string::npos ? string::npos : elemEnd - elemStart);
    }
    // When there are no more non-spaces, we are done.

    return v;
}
1
Timmmm

Verwenden von std::string_view und Eric Nieblers range-v3-Bibliothek:

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

Ich habe einen ganz anderen Ansatz als die anderen Lösungen, der einen großen Wert in einer Weise bietet, die den anderen Lösungen unterschiedlich fehlt, aber natürlich auch ihre eigenen Nachteile hat. Hier ist die funktionierende Implementierung mit dem Beispiel, <tag></tag> um Wörter herum zu setzen.

Dieses Problem lässt sich zunächst mit einer Schleife, ohne zusätzlichen Speicher und mit nur vier logischen Fällen lösen. Konzeptionell sind wir an Grenzen interessiert. Unser Code sollte das widerspiegeln: Lassen Sie uns die Zeichenfolge durchlaufen und zwei Zeichen gleichzeitig betrachten. Beachten Sie dabei, dass wir am Anfang und Ende der Zeichenfolge Sonderfälle haben.

Der Nachteil ist, dass wir die Implementierung schreiben müssen, was etwas ausführlich ist, aber meistens eine praktische Platte.

Der Vorteil ist, dass wir die Implementierung geschrieben haben, so dass es sehr einfach ist, sie an spezifische Anforderungen anzupassen, z. B. die Unterscheidung zwischen linken und schreibenden Word-Grenzen, die Verwendung eines Satzes von Trennzeichen oder die Behandlung anderer Fälle wie nicht-begrenzter oder falscher Positionen.

using namespace std;

#include <iostream>
#include <string>

#include <cctype>

typedef enum boundary_type_e {
    E_BOUNDARY_TYPE_ERROR = -1,
    E_BOUNDARY_TYPE_NONE,
    E_BOUNDARY_TYPE_LEFT,
    E_BOUNDARY_TYPE_RIGHT,
} boundary_type_t;

typedef struct boundary_s {
    boundary_type_t type;
    int pos;
} boundary_t;

bool is_delim_char(int c) {
    return isspace(c); // also compare against any other chars you want to use as delimiters
}

bool is_Word_char(int c) {
    return ' ' <= c && c <= '~' && !is_delim_char(c);
}

boundary_t maybe_Word_boundary(string str, int pos) {
    int len = str.length();
    if (pos < 0 || pos >= len) {
        return (boundary_t){.type = E_BOUNDARY_TYPE_ERROR};
    } else {
        if (pos == 0 && is_Word_char(str[pos])) {
            // if the first character is Word-y, we have a left boundary at the beginning
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos};
        } else if (pos == len - 1 && is_Word_char(str[pos])) {
            // if the last character is Word-y, we have a right boundary left of the null terminator
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        } else if (!is_Word_char(str[pos]) && is_Word_char(str[pos + 1])) {
            // if we have a delimiter followed by a Word char, we have a left boundary left of the Word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos + 1};
        } else if (is_Word_char(str[pos]) && !is_Word_char(str[pos + 1])) {
            // if we have a Word char followed by a delimiter, we have a right boundary right of the Word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        }
        return (boundary_t){.type = E_BOUNDARY_TYPE_NONE};
    }
}

int main() {
    string str;
    getline(cin, str);

    int len = str.length();
    for (int i = 0; i < len; i++) {
        boundary_t boundary = maybe_Word_boundary(str, i);
        if (boundary.type == E_BOUNDARY_TYPE_LEFT) {
            // whatever
        } else if (boundary.type == E_BOUNDARY_TYPE_RIGHT) {
            // whatever
        }
    }
}

Wie Sie sehen, ist der Code sehr einfach zu verstehen und zu verfeinern, und die tatsächliche Verwendung des Codes ist sehr kurz und einfach. Die Verwendung von C++ sollte uns nicht davon abhalten, den einfachsten und am einfachsten angepassten Code zu schreiben, selbst wenn dies bedeutet, dass die STL nicht verwendet wird. Ich denke, dies ist ein Beispiel für das, was Linus Torvalds "taste" nennen könnte, da wir die gesamte Logik, die wir nicht brauchen, während wir in einem Stil schreiben, der natürlich mehr Fälle verarbeitet, wenn und wenn die Notwendigkeit, mit ihnen umzugehen, entsteht.

Was diesen Code verbessern könnte, könnte die Verwendung von enum class sein, einen Funktionszeiger auf is_Word_char in maybe_Word_boundary akzeptieren, anstatt is_Word_char direkt aufzurufen, und ein Lambda übergeben.

0
okovko

Vielen Dank @Jairo Abdiel Toribio Cisneros. Es funktioniert für mich, aber Ihre Funktion gibt ein leeres Element zurück. Also für die Rückgabe ohne Leer habe ich folgendes editiert:

std::vector<std::string> split(std::string str, const char* delim) {
    std::vector<std::string> v;
    std::string tmp;

    for(std::string::const_iterator i = str.begin(); i <= str.end(); ++i) {
        if(*i != *delim && i != str.end()) {
            tmp += *i;
        } else {
            if (tmp.length() > 0) {
                v.Push_back(tmp);
            }
            tmp = "";
        }
    }

    return v;
}

Mit:

std::string s = "one:two::three";
std::string delim = ":";
std::vector<std::string> vv = split(s, delim.c_str());
0
Kakashi

meine allgemeine Implementierung für string und u32string ~ mit der boost::algorithm::split-Signatur.

template<typename CharT, typename UnaryPredicate>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           UnaryPredicate predicate)
{
    using ST = std::basic_string<CharT>;
    using std::swap;
    std::vector<ST> tmp_result;
    auto iter = s.cbegin(),
         end_iter = s.cend();
    while (true)
    {
        /**
         * Edge case: empty str -> Push an empty str and exit.
         */
        auto find_iter = find_if(iter, end_iter, predicate);
        tmp_result.emplace_back(iter, find_iter);
        if (find_iter == end_iter) { break; }
        iter = ++find_iter; 
    }
    swap(tmp_result, split_result);
}


template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const std::basic_string<CharT>& char_candidate)
{
    std::unordered_set<CharT> candidate_set(char_candidate.cbegin(),
                                            char_candidate.cend());
    auto predicate = [&candidate_set](const CharT& c) {
        return candidate_set.count(c) > 0U;
    };
    return split(split_result, s, predicate);
}

template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const CharT* literals)
{
    return split(split_result, s, std::basic_string<CharT>(literals));
}
0
小文件
#include <iostream>
#include <string>
#include <deque>

std::deque<std::string> split(
    const std::string& line, 
    std::string::value_type delimiter,
    bool skipEmpty = false
) {
    std::deque<std::string> parts{};

    if (!skipEmpty && !line.empty() && delimiter == line.at(0)) {
        parts.Push_back({});
    }

    for (const std::string::value_type& c : line) {
        if (
            (
                c == delimiter 
                &&
                (skipEmpty ? (!parts.empty() && !parts.back().empty()) : true)
            )
            ||
            (c != delimiter && parts.empty())
        ) {
            parts.Push_back({});
        }

        if (c != delimiter) {
            parts.back().Push_back(c);
        }
    }

    if (skipEmpty && !parts.empty() && parts.back().empty()) {
        parts.pop_back();
    }

    return parts;
}

void test(const std::string& line) {
    std::cout << line << std::endl;

    std::cout << "skipEmpty=0 |";
    for (const std::string& part : split(line, ':')) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << "skipEmpty=1 |";
    for (const std::string& part : split(line, ':', true)) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << std::endl;
}

int main() {
    test("foo:bar:::baz");
    test("");
    test("foo");
    test(":");
    test("::");
    test(":foo");
    test("::foo");
    test(":foo:");
    test(":foo::");

    return 0;
}

Ausgabe:

foo:bar:::baz
skipEmpty=0 |foo|bar|||baz|
skipEmpty=1 |foo|bar|baz|


skipEmpty=0 |
skipEmpty=1 |

foo
skipEmpty=0 |foo|
skipEmpty=1 |foo|

:
skipEmpty=0 |||
skipEmpty=1 |

::
skipEmpty=0 ||||
skipEmpty=1 |

:foo
skipEmpty=0 ||foo|
skipEmpty=1 |foo|

::foo
skipEmpty=0 |||foo|
skipEmpty=1 |foo|

:foo:
skipEmpty=0 ||foo||
skipEmpty=1 |foo|

:foo::
skipEmpty=0 ||foo|||
skipEmpty=1 |foo|
0
Oleg

wenn Sie die Zeichenfolge nach Zeichen trennen möchten, können Sie sie verwenden

#include<iostream>
#include<string>
#include<vector>
#include<iterator>
#include<sstream>
#include<string>

using namespace std;
void replaceOtherChars(string &input, vector<char> &dividers)
{
    const char divider = dividers.at(0);
    int replaceIndex = 0;
    vector<char>::iterator it_begin = dividers.begin()+1,
        it_end= dividers.end();
    for(;it_begin!=it_end;++it_begin)
    {
        replaceIndex = 0;
        while(true)
        {
            replaceIndex=input.find_first_of(*it_begin,replaceIndex);
            if(replaceIndex==-1)
                break;
            input.at(replaceIndex)=divider;
        }
    }
}
vector<string> split(string str, vector<char> chars, bool missEmptySpace =true )
{
    vector<string> result;
    const char divider = chars.at(0);
    replaceOtherChars(str,chars);
    stringstream stream;
    stream<<str;    
    string temp;
    while(getline(stream,temp,divider))
    {
        if(missEmptySpace && temp.empty())
            continue;
        result.Push_back(temp);
    }
    return result;
}
int main()
{
    string str ="milk, pigs.... hot-dogs ";
    vector<char> arr;
    arr.Push_back(' '); arr.Push_back(','); arr.Push_back('.');
    vector<string> result = split(str,arr);
    vector<string>::iterator it_begin= result.begin(),
        it_end= result.end();
    for(;it_begin!=it_end;++it_begin)
    {
        cout<<*it_begin<<endl;
    }
return 0;
}
0
h.o.m.a.n

Meine Implementierung kann eine alternative Lösung sein:

std::vector<std::wstring> SplitString(const std::wstring & String, const std::wstring & Seperator)
{
    std::vector<std::wstring> Lines;
    size_t stSearchPos = 0;
    size_t stFoundPos;
    while (stSearchPos < String.size() - 1)
    {
        stFoundPos = String.find(Seperator, stSearchPos);
        stFoundPos = (stFoundPos == std::string::npos) ? String.size() : stFoundPos;
        Lines.Push_back(String.substr(stSearchPos, stFoundPos - stSearchPos));
        stSearchPos = stFoundPos + Seperator.size();
    }
    return Lines;
}

Testcode:

std::wstring MyString(L"Part 1SEPsecond partSEPlast partSEPend");
std::vector<std::wstring> Parts = IniFile::SplitString(MyString, L"SEP");
std::wcout << L"The string: " << MyString << std::endl;
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}
std::wcout << std::endl;
MyString = L"this,time,a,comma separated,string";
std::wcout << L"The string: " << MyString << std::endl;
Parts = IniFile::SplitString(MyString, L",");
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}

Ausgabe des Testcodes:

The string: Part 1SEPsecond partSEPlast partSEPend
Part 1<---
second part<---
last part<---
end<---

The string: this,time,a,comma separated,string
this<---
time<---
a<---
comma separated<---
string<---
0
hkBattousai

Dies ist eine Erweiterung einer der Top-Antworten. Es unterstützt jetzt das Setzen einer maximalen Anzahl von zurückgegebenen Elementen, N. Das letzte Bit der Zeichenfolge endet im N-ten Element. Der Parameter MAXELEMENTS ist optional. Wenn der Standardwert 0 festgelegt ist, wird eine Anzahl von unlimited Elementen zurückgegeben. :-)

.h:

class Myneatclass {
public:
    static std::vector<std::string>& split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS = 0);
    static std::vector<std::string> split(const std::string &s, char delim, const size_t MAXELEMENTS = 0);
};

.cpp:

std::vector<std::string>& Myneatclass::split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.Push_back(item);
        if (MAXELEMENTS > 0 && !ss.eof() && elems.size() + 1 >= MAXELEMENTS) {
            std::getline(ss, item);
            elems.Push_back(item);
            break;
        }
    }
    return elems;
}
std::vector<std::string> Myneatclass::split(const std::string &s, char delim, const size_t MAXELEMENTS) {
    std::vector<std::string> elems;
    split(s, delim, elems, MAXELEMENTS);
    return elems;
}
0
Jonny

zur Party hier sehr spät, weiß ich, aber ich habe über die eleganteste Art und Weise nachgedacht, dies zu tun, wenn Sie eine Auswahl von Trennzeichen anstelle von Leerzeichen erhalten und nichts anderes als die Standardbibliothek verwenden.

Hier sind meine Gedanken:

Wörter in einen String-Vektor durch eine Folge von Trennzeichen aufteilen:

template<class Container>
std::vector<std::string> split_by_delimiters(const std::string& input, const Container& delimiters)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if(current, end(input), not_in(delimiters));
        if (first == end(input)) break;
        auto last = find_if(first, end(input), is_in(delimiters));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

um den anderen Weg zu teilen, geben Sie eine Folge gültiger Zeichen an:

template<class Container>
std::vector<std::string> split_by_valid_chars(const std::string& input, const Container& valid_chars)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if(current, end(input), is_in(valid_chars));
        if (first == end(input)) break;
        auto last = find_if(first, end(input), not_in(valid_chars));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

is_in und not_in sind folgendermaßen definiert:

namespace detail {
    template<class Container>
    struct is_in {
        is_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) != end(_charset);
        }

        const Container& _charset;
    };

    template<class Container>
    struct not_in {
        not_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) == end(_charset);
        }

        const Container& _charset;
    };

}

template<class Container>
detail::not_in<Container> not_in(const Container& c)
{
    return detail::not_in<Container>(c);
}

template<class Container>
detail::is_in<Container> is_in(const Container& c)
{
    return detail::is_in<Container>(c);
}
0
Richard Hodges