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?
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
string sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
Antworten:
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.
Anstatt die extrahierten Token in einen Ausgabestream zu kopieren, könnte man sie mit demselben generischen
copy
Algorithmus in einen Container einfügen .... oder erstellen Sie
vector
direkt:quelle
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.
Beachten Sie, dass bei dieser Lösung keine leeren Token übersprungen werden. Im Folgenden werden 4 Elemente gefunden, von denen eines leer ist:
quelle
empty()
überprüfen Sieif (!item.empty()) elems.push_back(item)
->
?f(split(s, d, v))
und trotzdem den Vorteil einer vorab zugewiesenen Funktion haben,vector
wenn Sie möchten.Eine mögliche Lösung mit Boost könnte sein:
Dieser Ansatz ist möglicherweise sogar schneller als der
stringstream
Ansatz. 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 .
quelle
quelle
getline
in derwhile
Bedingung verwenden, z. B. durch Kommas teilen, verwendenwhile(getline(ss, buff, ','))
.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.):
Normalerweise verwende ich
std::vector<std::string>
Typen als zweiten Parameter (ContainerT
) ... aber eslist<>
ist viel schneller alsvector<>
wenn kein direkter Zugriff erforderlich ist. Sie können sogar Ihre eigene Zeichenfolgenklasse erstellen und so etwas wie "std::list<subString>
wosubString
keine 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.quelle
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.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, esstr.find_first_of
sollte sich ändernstr.find_first
, aber ich könnte mich irren ... kann nicht testen)Hier ist eine andere Lösung. Es ist kompakt und ziemlich effizient:
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:
Wenn das Teilen einer Zeichenfolge an mehreren Trennzeichen beim Überspringen leerer Token gewünscht wird, kann diese Version verwendet werden:
quelle
Dies ist meine Lieblingsmethode, um eine Zeichenfolge zu durchlaufen. Sie können pro Wort tun, was Sie wollen.
quelle
word
als zu deklarierenchar
?stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;
Dies ähnelt der Frage zum Stapelüberlauf. Wie kann ich eine Zeichenfolge in C ++ tokenisieren? .
quelle
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.
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.quelle
Die STL verfügt noch nicht über eine solche Methode.
Sie können jedoch entweder die C-
strtok()
Funktion mithilfe desstd::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" ):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
typedef
aufgerufenen Iterator oder<<
keine Überladung implementiert, bedeutet der Operator nicht, dass es sich um schlechten Code handelt. Ich benutze ziemlich häufig C-Funktionen. Zum Beispielprintf
undscanf
beide sind schneller alsstd::cin
undstd::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 .
quelle
Hier ist eine Split-Funktion, die:
ignoriert leere Token (kann leicht geändert werden)
Anwendungsbeispiel:
quelle
Ich habe eine 2-Zeilen-Lösung für dieses Problem:
Anstatt zu drucken, können Sie es dann in einen Vektor einfügen.
quelle
Noch ein flexibler und schneller Weg
So verwenden Sie es mit einem Vektor von Zeichenfolgen (Bearbeiten: Da jemand darauf hingewiesen hat, STL-Klassen nicht zu erben ... hrmf;)):
Das ist es! Und das ist nur eine Möglichkeit, den Tokenizer zu verwenden, beispielsweise wie man nur Wörter zählt:
Begrenzt durch Vorstellungskraft;)
quelle
Appender
Anmerkung : „Warum sollen wir nicht eine Klasse von STL - Klassen erben?“Hier ist eine einfache Lösung, die nur die Standard-Regex-Bibliothek verwendet
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:
Die
"[\\s,]+"
Prüfung auf Leerzeichen (\\s
) und Kommas (,
).Beachten Sie, wenn Sie
wstring
statt teilen möchtenstring
,std::regex
aufstd::wregex
sregex_token_iterator
aufwsregex_token_iterator
Beachten Sie, dass Sie abhängig von Ihrem Compiler möglicherweise auch das Zeichenfolgenargument als Referenz verwenden möchten.
quelle
R"([\s,]+)"
.Die Verwendung
std::stringstream
wie Sie funktioniert einwandfrei und macht genau das, was Sie wollten. Wenn Sie jedoch nur nach einer anderen Vorgehensweise suchen, können Siestd::find()
/std::find_first_of()
und verwendenstd::string::substr()
.Hier ist ein Beispiel:
quelle
prev_pos = pos += delimiter.length();
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:
quelle
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.)
quelle
Es gibt eine Funktion namens
strtok
.quelle
strtok
stammt aus der C-Standardbibliothek, nicht aus C ++. Die Verwendung in Multithread-Programmen ist nicht sicher. Es ändert die Eingabezeichenfolge.strtok
wä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.aspDer Stringstream kann praktisch sein, wenn Sie den String nach Nicht-Leerzeichen analysieren müssen:
quelle
Bisher habe ich die in Boost verwendet , aber ich brauchte etwas, das nicht davon abhängt, also bin ich dazu gekommen:
Ein guter Punkt ist, dass
separators
Sie mehr als ein Zeichen übergeben können.quelle
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.
Das Toolkit ist viel flexibler als dieses einfache Beispiel zeigt, aber seine Nützlichkeit beim Parsen einer Zeichenfolge in nützliche Elemente ist unglaublich.
quelle
Kurz und elegant
kann eine beliebige Zeichenfolge als Trennzeichen verwenden, kann auch mit Binärdaten verwendet werden (std :: string unterstützt Binärdaten, einschließlich Nullen)
mit:
Ausgabe:
quelle
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:
StringSplitter.cpp:
Beispiele:
Wird ausgegeben:
Dies
ist
ein
Beispiel für eine
Zeichenfolge
So behalten Sie leere Einträge bei (standardmäßig werden Leergut ausgeschlossen):
Das Ziel war es, es der Split () -Methode von C # ähnlich zu machen, bei der das Teilen eines Strings so einfach ist wie:
Ich hoffe, jemand anderes kann dies genauso nützlich finden wie ich.
quelle
Was ist damit:
quelle
Diese Antwort nimmt die Zeichenfolge und fügt sie in einen Vektor von Zeichenfolgen ein. Es verwendet die Boost-Bibliothek.
quelle
Hier ist eine andere Möglichkeit, es zu tun.
quelle
Ich verwende für diese Aufgabe gerne die Boost / Regex-Methoden, da sie maximale Flexibilität für die Angabe der Aufteilungskriterien bieten.
quelle
Vor kurzem musste ich ein Wort mit Kamelhülle in Unterwörter aufteilen. Es gibt keine Trennzeichen, nur obere Zeichen.
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::upper
sollte wirklich als Funktionsvorlagenargument übergeben werden. Dann verallgemeinert die mehr von dieser Funktion aufspalten können Trennzeichen wie","
,";"
oder" "
auch.quelle
std::isupper
konnten als Argument übergeben werden, nichtstd::upper
. Zweitens setzen Sie einetypename
vor dieString::const_iterator
.quelle
Mit
std::string_view
und Eric Nieblerrange-v3
Bibliothek:https://wandbox.org/permlink/kW5lwRCL1pxjp2pW
Durch Verwendung einer Bereichsschleife
for
anstelle einesranges::for_each
Algorithmus:quelle