Wie iteriere ich über die Wörter einer Zeichenfolge?


2986

Ich versuche, über die Wörter einer Zeichenfolge zu iterieren.

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

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

Die beste Lösung, die ich derzeit 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 eine elegantere Möglichkeit, dies zu tun?


617
Alter ... Eleganz ist nur eine ausgefallene Art, in meinem Buch "Effizienz, die hübsch aussieht" zu sagen.

14
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
Pyon

21
@Eduardo: Das ist auch falsch ... Sie müssen iss zwischen dem Versuch, einen anderen Wert zu streamen, und der Verwendung dieses Wertes testen, dhstring sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
Tony Delroy

9
Verschiedene Optionen in C ++, um dies standardmäßig zu tun: cplusplus.com/faq/sequences/strings/split
hB0

14
Eleganz ist mehr als nur Effizienz. Zu den eleganten Attributen gehören eine geringe Zeilenanzahl und eine hohe Lesbarkeit. IMHO Elegance ist kein Proxy für Effizienz, sondern für Wartbarkeit.
Matt

Antworten:


1369

Für das, was es wert ist, gibt es eine andere Möglichkeit, Token aus einer Eingabezeichenfolge zu extrahieren, wobei nur Standardbibliotheksfunktionen verwendet werden. Es ist ein Beispiel für die Kraft und Eleganz hinter dem Design der 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"));
}

Anstatt die extrahierten Token in einen Ausgabestream zu kopieren, könnte man sie mit demselben generischen copyAlgorithmus in einen Container einfügen .

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

... oder erstellen Sie vectordirekt:

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

164
Ist es möglich, hierfür ein Trennzeichen anzugeben? Wie zum Beispiel durch Kommas aufteilen?
l3dx

15
@ Jonathan: \ n ist in diesem Fall nicht das Trennzeichen, sondern das Trennzeichen für die Ausgabe an cout.
Huy

772
Dies ist eine schlechte Lösung, da kein anderes Trennzeichen erforderlich ist, daher nicht skalierbar und nicht wartbar.
HelloWorld

37
Tatsächlich kann dies mit anderen Trennzeichen gut funktionieren (obwohl einige etwas hässlich sind). Sie erstellen eine ctype-Facette, die die gewünschten Trennzeichen als Leerzeichen klassifiziert, erstellen ein Gebietsschema, das diese Facette enthält, und fügen dem Stringstream dieses Gebietsschema hinzu, bevor Sie Zeichenfolgen extrahieren.
Jerry Coffin

53
@Kinderchocolate "Es kann angenommen werden, dass die Zeichenfolge aus durch Leerzeichen getrennten Wörtern besteht" - Hmm, das klingt nicht nach einer schlechten Lösung für das Problem der Frage. "nicht skalierbar und nicht wartbar" - Hah, nett.
Christian Rau

2426

Ich benutze dies, um die Zeichenfolge durch ein Trennzeichen zu teilen. 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::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;
}

Beachten Sie, dass bei dieser Lösung keine leeren Token übersprungen werden. Im Folgenden werden 4 Elemente gefunden, von denen eines leer ist:

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

86
Um zu vermeiden, dass leere Token übersprungen werden, empty()überprüfen Sie if (!item.empty()) elems.push_back(item)
Folgendes

11
Wie wäre es mit dem Delim, das zwei Zeichen enthält ->?
Heldhuyongtao

7
@herohuyongtao, diese Lösung funktioniert nur für einzelne Zeichenbegrenzer.
Evan Teran

4
@JeshwanthKumarNK, es ist nicht notwendig, aber Sie können damit beispielsweise das Ergebnis direkt an eine Funktion wie diese übergeben: f(split(s, d, v))und trotzdem den Vorteil einer vorab zugewiesenen Funktion haben, vectorwenn Sie möchten.
Evan Teran

8
Vorsichtsmaßnahme: split ("eins: zwei :: drei", ':') und split ("eins: zwei :: drei:", ':') geben denselben Wert zurück.
Dshin

834

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 sogar schneller als der stringstreamAnsatz. Und da dies eine generische Vorlagenfunktion ist, kann sie verwendet werden, um andere Arten von Zeichenfolgen (wchar usw. oder UTF-8) unter Verwendung aller Arten von Trennzeichen zu teilen.

Einzelheiten finden Sie in der Dokumentation .


35
Die Geschwindigkeit spielt hier keine Rolle, da beide Fälle viel langsamer sind als eine strtok-ähnliche Funktion.
Tom

45
Und für diejenigen, die noch keinen Boost haben ... bcp kopiert dafür über 1.000 Dateien :)
Roman Starkov

12
Warnung: Wenn eine leere Zeichenfolge ("") angegeben wird, gibt diese Methode einen Vektor zurück, der die Zeichenfolge "" enthält. Fügen Sie also vor dem Teilen ein "if (! String_to_split.empty ())" hinzu.
Offirmo

29
@ Ian Embedded-Entwickler verwenden nicht alle Boost.
ACK_stoverflow

31
als Nachtrag: Ich benutze Boost nur, wenn ich muss. Normalerweise ziehe ich es vor, meine eigene Codebibliothek zu erweitern, die eigenständig und portabel ist, damit ich einen kleinen präzisen spezifischen Code erzielen kann, der ein bestimmtes Ziel erreicht. Auf diese Weise ist der Code nicht öffentlich, performant, trivial und portabel. Boost hat seinen Platz, aber ich würde vorschlagen, dass es ein bisschen übertrieben ist, um Saiten zu kennzeichnen: Sie würden nicht Ihr ganzes Haus zu einem Ingenieurbüro transportieren lassen, um einen neuen Nagel in die Wand hämmern zu lassen, um ein Bild aufzuhängen ... sie könnten es tun Sehr gut, aber die Vorteile werden bei weitem durch die Nachteile aufgewogen.
GMasucci

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

12
Sie können auch auf andere Trennzeichen aufteilen, wenn Sie getlinein der whileBedingung verwenden, z. B. durch Kommas teilen, verwenden while(getline(ss, buff, ',')).
Ali

181

Für diejenigen, bei denen es nicht gut ist, die gesamte Effizienz für die Codegröße zu opfern und "effizient" als eine Art Eleganz zu betrachten, sollte Folgendes einen Sweet Spot treffen (und ich denke, die Template-Container-Klasse ist eine unglaublich elegante Ergänzung.):

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 verwende ich std::vector<std::string>Typen als zweiten Parameter ( ContainerT) ... aber es list<>ist viel schneller als vector<>wenn kein direkter Zugriff erforderlich ist. Sie können sogar Ihre eigene Zeichenfolgenklasse erstellen und so etwas wie " std::list<subString>wo subStringkeine Kopien für unglaubliche Geschwindigkeit erstellt" verwenden erhöht sich.

Es ist mehr als doppelt so schnell wie das schnellste Token auf dieser Seite und fast fünfmal schneller als einige andere. Mit den perfekten Parametertypen können Sie auch alle Zeichenfolgen- und Listenkopien entfernen, um die Geschwindigkeit zu erhöhen.

Darüber hinaus führt es nicht die (äußerst ineffiziente) Rückgabe des Ergebnisses durch, sondern übergibt die Token als Referenz, sodass Sie auf Wunsch auch Token mit mehreren Aufrufen erstellen können.

Zuletzt können Sie festlegen, ob leere Token über einen letzten optionalen Parameter 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 jedoch flexibel genug, um einige dieser fremden Typen auf natürliche Weise akzeptieren zu können.


5
Ich bin ein ziemlicher Fan davon, aber für g ++ (und wahrscheinlich eine gute Praxis) möchte jeder, der dies verwendet, typedefs und typenames: typedef ContainerT Base; typedef typename Base::value_type ValueType; typedef typename ValueType::size_type SizeType; Dann ersetzen Sie die value_type und size_types entsprechend.
aws

11
Für diejenigen von uns, für die das Vorlagenmaterial und der erste Kommentar völlig fremd sind, wäre ein Verwendungsbeispiel mit den erforderlichen Includes sehr schön.
Wes Miller

3
Ahh gut, ich habe es herausgefunden. Ich habe die C ++ - Zeilen aus dem Kommentar von aws in den Funktionskörper von tokenize () eingefügt und dann die Zeilen tokens.push_back () bearbeitet, um den ContainerT :: value_type in nur ValueType zu ändern, und (ContainerT :: value_type :: size_type) in ( SizeType). Behoben, dass die Bits, über die g ++ gejammert hatte, behoben waren. Rufen Sie es einfach als Tokenize auf (some_string, some_vector).
Wes Miller

2
Abgesehen von einigen Leistungstests für Beispieldaten habe ich diese in erster Linie auf so wenige Anweisungen wie möglich und auch auf so wenig Speicherkopien wie möglich reduziert, die durch die Verwendung einer Teilzeichenfolgenklasse ermöglicht werden, die nur auf Offsets / Längen in anderen Zeichenfolgen verweist. (Ich habe meine eigenen gerollt, aber es gibt einige andere Implementierungen). Leider kann man nicht zu viel anderes tun, um dies zu verbessern, aber schrittweise Erhöhungen waren möglich.
Marius

3
Das ist die richtige Ausgabe für wann trimEmpty = true. Beachten Sie, dass "abo"dies in dieser Antwort kein Trennzeichen ist, sondern die Liste der Trennzeichen. Es wäre einfach, es so zu ändern, dass es eine einzelne Zeichenfolge mit Trennzeichen enthält (ich denke, es str.find_first_ofsollte sich ändern str.find_first, aber ich könnte mich irren ... kann nicht testen)
Marius

158

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 als Vorlage für String-Trennzeichen, breite Strings usw. verwendet werden.

Beachten Sie, dass das Teilen ""zu einer einzelnen leeren Zeichenfolge führt und das Teilen ","(dh Sep.) zu zwei leeren Zeichenfolgen führt.

Es kann auch einfach 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 das Teilen einer Zeichenfolge an mehreren Trennzeichen beim Überspringen leerer Token gewünscht wird, 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;
}

10
Die erste Version ist einfach und erledigt die Arbeit perfekt. Die einzige Änderung, die ich vornehmen würde, wäre, das Ergebnis direkt zurückzugeben, anstatt es als Parameter zu übergeben.
Gregschlom

2
Die Ausgabe wird als Parameter für die Effizienz übergeben. Wenn das Ergebnis zurückgegeben würde, wäre entweder eine Kopie des Vektors oder eine Heap-Zuordnung erforderlich, die dann freigegeben werden müsste.
Alec Thomas

2
Ein kleiner Nachtrag zu meinem obigen Kommentar: Diese Funktion könnte den Vektor ohne Strafe zurückgeben, wenn die C ++ 11-Verschiebungssemantik verwendet wird.
Alec Thomas

7
@AlecThomas: Würden die meisten Compiler die Rückgabekopie nicht schon vor C ++ 11 über NRVO optimieren? (+1 sowieso; sehr prägnant)
Marcelo Cantos

11
Von allen Antworten scheint dies eine der attraktivsten und flexibelsten zu sein. Zusammen mit der getline mit einem Trennzeichen, obwohl es eine weniger offensichtliche Lösung ist. Hat der c ++ 11 Standard nichts dafür? Unterstützt c ++ 11 heutzutage Lochkarten?
Spacen Jasset

123

Dies ist meine Lieblingsmethode, um eine Zeichenfolge zu durchlaufen. Sie können pro Wort tun, was Sie wollen.

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

istringstream iss(line, istringstream::in);

while( iss >> word )     
{
    // Do something on `word` here...
}

Ist es möglich, wordals zu deklarieren char?
Abatishchev

Sorry abatishchev, C ++ ist nicht meine Stärke. Aber ich stelle mir vor, es wäre nicht schwierig, eine innere Schleife hinzuzufügen, um jedes Zeichen in jedem Wort zu durchlaufen. Aber im Moment glaube ich, dass die aktuelle Schleife von Leerzeichen für die Worttrennung abhängt. Es sei denn, Sie wissen, dass sich zwischen jedem Leerzeichen nur ein einziges Zeichen befindet. In diesem Fall können Sie einfach "Wort" in ein Zeichen umwandeln. Tut mir leid, ich kann Ihnen nicht weiterhelfen. Ich wollte mein C ++
gnomed

11
Wenn Sie ein Wort als Zeichen deklarieren, wird es über jedes Nicht-Leerzeichen durchlaufen. Es ist einfach genug zu versuchen:stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;
Wayne Werner

79

Dies ähnelt der Frage zum Stapelüberlauf. Wie kann ich eine Zeichenfolge in C ++ tokenisieren? .

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

Wird dadurch eine Kopie aller Token erstellt oder wird nur die Start- und Endposition des aktuellen Tokens beibehalten?
Einpoklum

66

Ich mag das Folgende, weil es die Ergebnisse in einen Vektor einfügt, eine Zeichenfolge als Trennzeichen unterstützt und die Kontrolle über das Beibehalten leerer Werte gibt. Aber dann sieht es 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. Und wenn mit "Leerraum" wirklich jede Art von Leerraum gemeint ist, is_any_of()funktioniert die Aufteilung von Boost mit funktioniert hervorragend.


Endlich eine Lösung, die leere Token auf beiden Seiten des Strings korrekt behandelt
fmuecke

53

Die STL verfügt noch nicht über eine solche Methode.

Sie können jedoch entweder die C- strtok()Funktion mithilfe des std::string::c_str()Elements verwenden oder 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 werde es erklären.

Und nur weil es keinen typedefaufgerufenen Iterator oder <<keine Überladung implementiert, bedeutet der Operator nicht, dass es sich um schlechten Code handelt. Ich benutze ziemlich häufig C-Funktionen. Zum Beispiel printfund scanfbeide sind schneller als std::cinund std::cout(signifikant), diefopen Syntax für Binärtypen viel benutzerfreundlicher und sie neigen auch dazu, kleinere EXEs zu erzeugen.

Lassen Sie sich von diesem "Elegance over Performance" -Deal nicht verkaufen .


Ich kenne die C-String-Funktionen und auch die Leistungsprobleme (beides habe ich in meiner Frage notiert). Für diese spezielle Frage suche ich jedoch nach einer eleganten C ++ - Lösung.
Ashwin Nanjappa

11
@ Nelson LaQuet: Lassen Sie mich raten: Weil strtok nicht wiedereintrittsfähig ist?
Paercebal

40
@ Nelson übergibt niemals string.c_str () an strtok! strtok verwirft die Eingabezeichenfolge (fügt '\ 0' Zeichen ein, um jedes foudn-Trennzeichen zu ersetzen) und c_str () gibt eine nicht modifizierbare Zeichenfolge zurück.
Evan Teran

3
@ Nelson: Dieses Array muss in Ihrem letzten Kommentar die Größe str.size () + 1 haben. Aber ich stimme Ihrer These zu, dass es dumm ist, C-Funktionen aus "ästhetischen" Gründen zu vermeiden.
j_random_hacker

2
@paulm: Nein, die Langsamkeit von C ++ - Streams wird durch Facetten verursacht. Sie sind immer noch langsamer als die Funktionen von stdio.h, selbst wenn die Synchronisierung deaktiviert ist (und bei Stringstreams, die nicht synchronisiert werden können).
Ben Voigt

42

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

Anwendungsbeispiel:

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

Sie haben vergessen, zur Verwendungsliste hinzuzufügen: "extrem ineffizient"
Xander Tulip

1
@ XanderTulip, kannst du konstruktiver sein und erklären, wie oder warum?
Marco M.

3
@ XanderTulip: Ich nehme an, Sie beziehen sich darauf und geben den Vektor nach Wert zurück. Die Return-Value-Optimierung (RVO, google it) sollte sich darum kümmern. Auch in C ++ 11 können Sie per Verschiebungsreferenz zurückkehren.
Joseph Garvin

3
Dies kann tatsächlich weiter optimiert werden: Anstelle von .push_back (str.substr (...)) kann .emplace_back (str, start, pos - start) verwendet werden. Auf diese Weise wird das String-Objekt im Container erstellt und wir vermeiden eine Verschiebungsoperation + andere Spielereien, die von der .substr-Funktion ausgeführt werden.
Mihai Bişog

@zoopp ja. Gute Idee. VS10 hatte keine emplace_back-Unterstützung, als ich das schrieb. Ich werde meine Antwort aktualisieren. Vielen Dank
Marco M.

36

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 dann in einen Vektor einfügen.


35

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, beispielsweise wie man nur Wörter zählt:

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



32

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 Überprüfung auf mehrere Argumente (Leerzeichen, Kommas usw.).

Normalerweise überprüfe ich nur, ob Leerzeichen und Kommas geteilt werden sollen, daher habe ich auch diese Standardfunktion:

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

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

    return Tokenize( str, re );
}

Die "[\\s,]+"Prüfung auf Leerzeichen ( \\s) und Kommas (, ).

Beachten Sie, wenn Sie wstringstatt teilen möchten string,

  • ändere alles std::regexaufstd::wregex
  • ändere alles sregex_token_iteratoraufwsregex_token_iterator

Beachten Sie, dass Sie abhängig von Ihrem Compiler möglicherweise auch das Zeichenfolgenargument als Referenz verwenden möchten.


Dies wäre meine Lieblingsantwort gewesen, aber std :: regex ist in GCC 4.8 fehlerhaft. Sie sagten, dass sie es in GCC 4.9 korrekt implementiert haben. Ich gebe Ihnen immer noch meine +1
mchiasson

1
Dies ist mein Favorit mit geringfügigen Änderungen: Der Vektor wurde wie gesagt als Referenz zurückgegeben, und die Argumente "str" ​​und "regex" wurden ebenfalls von Referenzen übergeben. Danke.
QuantumKarl

1
Rohe Zeichenfolgen sind beim Umgang mit Regex-Mustern sehr nützlich. Auf diese Weise müssen Sie die Escape-Sequenzen nicht verwenden ... Sie können sie einfach verwenden R"([\s,]+)".
Sam

26

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

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

Dies funktioniert nur für Einzelzeichenbegrenzer. Eine einfache Änderung lässt es mit Multicharakter arbeiten:prev_pos = pos += delimiter.length();
David Doria

25

Wenn Sie Boost verwenden möchten, aber eine ganze Zeichenfolge als Trennzeichen verwenden möchten (anstelle einzelner Zeichen wie in den meisten zuvor vorgeschlagenen Lösungen), können Sie das verwenden boost_split_iterator.

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

20

Hier ist eine Regex-Lösung, die nur die Standard-Regex-Bibliothek verwendet. (Ich bin ein wenig verrostet, daher kann es zu einigen Syntaxfehlern kommen, aber dies 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
}

Ähnliche Antworten mit vielleicht besserem Regex-Ansatz: hier und hier .
Nobar

20

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

3
strtokstammt aus der C-Standardbibliothek, nicht aus C ++. Die Verwendung in Multithread-Programmen ist nicht sicher. Es ändert die Eingabezeichenfolge.
Kevin Panko

13
Da der Zeichenzeiger vom ersten Aufruf an in einer statischen Variablen gespeichert wird, merkt er sich bei den nachfolgenden Aufrufen, wenn NULL übergeben wird, welcher Zeiger verwendet werden soll. Wenn ein zweiter Thread aufruft, strtokwährend ein anderer Thread noch verarbeitet wird, wird dieser Zeichenzeiger überschrieben, und beide Threads führen dann zu falschen Ergebnissen. mkssoftware.com/docs/man3/strtok.3.asp
Kevin Panko

1
Wie bereits erwähnt, ist strtok unsicher und sogar in C wird strtok_r zur Verwendung empfohlen
Systemfehler

4
strtok_r kann verwendet werden, wenn Sie sich in einem Codeabschnitt befinden, auf den zugegriffen werden kann. Dies ist die einzige Lösung von all dem, die kein "Leitungsrauschen" ist und ein Beweis dafür ist, was genau mit c ++ falsch ist
Erik Aronesty

Aktualisiert, damit keine Einwände aus Gründen der Thread-Sicherheit von C ++ - Wonks erhoben werden können.
Erik Aronesty

17

Der Stringstream kann praktisch sein, wenn Sie den String nach Nicht-Leerzeichen 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, ';')

14

Bisher habe ich die in Boost verwendet , aber ich brauchte etwas, das nicht davon abhängt, also bin ich dazu gekommen:

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 separatorsSie mehr als ein Zeichen übergeben können.


13

Ich habe meine eigene mit strtok gewürfelt und Boost verwendet, um eine Saite zu teilen. 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 ist viel flexibler als dieses einfache Beispiel zeigt, aber seine Nützlichkeit beim Parsen einer Zeichenfolge in nützliche Elemente ist unglaublich.


13

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 eine beliebige Zeichenfolge als Trennzeichen verwenden, kann auch mit Binärdaten verwendet werden (std :: string unterstützt Binärdaten, einschließlich Nullen)

mit:

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

Ausgabe:

this
is
!example!string

1
Ich mag diese Lösung, weil das Trennzeichen eine Zeichenfolge und kein Zeichen sein kann. Es ändert jedoch die Zeichenfolge an Ort und Stelle, sodass die Erstellung einer Kopie der ursprünglichen Zeichenfolge erzwungen wird.
Alessandro Teruzzi

11

Ich habe das gemacht, weil ich einen einfachen Weg brauchte, um Strings und C-basierte Strings zu teilen ... Hoffentlich kann es auch jemand anderes nützlich finden. Außerdem sind keine Token erforderlich, und Sie können Felder als Trennzeichen verwenden. Dies ist ein weiterer Schlüssel, den ich benötigte.

Ich bin sicher, es gibt Verbesserungen, die vorgenommen werden können, um die Eleganz noch weiter zu verbessern, und bitte 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:

Dies
ist
ein
Beispiel für eine
Zeichenfolge

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

So behalten Sie leere Einträge bei (standardmäßig werden Leergut 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, bei der das Teilen eines Strings 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 dies genauso nützlich finden wie ich.


10

Was ist damit:

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

Dies ist hier die beste Antwort, wenn Sie nur ein einzelnes Trennzeichen aufteilen möchten. Die ursprüngliche Frage wollte jedoch auf Leerzeichen aufgeteilt werden, dh eine beliebige Kombination aus einem oder mehreren aufeinanderfolgenden Leerzeichen oder Tabulatoren. Sie haben tatsächlich stackoverflow.com/questions/53849
Oktalist

10

Diese Antwort nimmt die Zeichenfolge und fügt sie in einen Vektor von Zeichenfolgen ein. 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

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

Ich verwende für diese Aufgabe gerne die Boost / Regex-Methoden, da sie maximale Flexibilität für die Angabe 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

Vor kurzem musste ich ein Wort mit Kamelhülle in Unterwörter aufteilen. 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;
}

Dies teilt beispielsweise "AQueryTrades" in "A", "Query" und "Trades" auf. Die Funktion arbeitet mit schmalen und breiten Zeichenfolgen. Da es das aktuelle Gebietsschema respektiert, teilt es die "RaumfahrtÜberwachungsVerordnung" in "Raumfahrt", "Überwachung" und "Verordnung" auf.

Hinweis std::uppersollte wirklich als Funktionsvorlagenargument übergeben werden. Dann verallgemeinert die mehr von dieser Funktion aufspalten können Trennzeichen wie ",", ";"oder " "auch.


2
Es gab 2 Umdrehungen. Das ist schön. Scheint, als hätte mein Englisch zu viel "Deutsch". Der Revisionist hat jedoch zwei kleinere Fehler nicht behoben, vielleicht weil sie sowieso offensichtlich waren: Sie std::isupperkonnten als Argument übergeben werden, nicht std::upper. Zweitens setzen Sie eine typenamevor die String::const_iterator.
Andreas Spindler

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

Mit std::string_viewund Eric Nieblerrange-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";}
    );
}

Durch Verwendung einer Bereichsschleife foranstelle eines ranges::for_eachAlgorithmus:

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

Yepp, die Reichweite für basiert sieht besser aus - ich stimme zu
Porsche9II
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.