Ich kann nicht glauben, dass diese Routineaufgabe in c ++
wfbarksdale
6
In C ++ gibt es keine Kopfschmerzen - es gibt verschiedene Möglichkeiten, dies zu erreichen. Programmierer sind sich c ++ weniger bewusst als c # - es geht um Marketing und Investitionen ... siehe dies für verschiedene c ++ - Optionen, um dasselbe zu erreichen: cplusplus.com/faq/sequences/strings/split
hB0
9
@ hB0 viele Fragen durch Antworten und immer noch nicht entscheiden Mittel ist Kopfschmerzen. Der eine braucht diese Bibliothek, der andere ist nur für Leerzeichen, der andere behandelt keine Leerzeichen.
C ++ - Standardbibliotheksalgorithmen basieren ziemlich universell auf Iteratoren und nicht auf konkreten Containern. Leider ist es schwierig, eine Java-ähnliche splitFunktion in der C ++ - Standardbibliothek bereitzustellen , obwohl niemand argumentiert, dass dies praktisch wäre. Aber wie würde die Rückgabe aussehen? std::vector<std::basic_string<…>>? Vielleicht, aber dann sind wir gezwungen, (möglicherweise redundante und kostspielige) Zuweisungen durchzuführen.
Stattdessen bietet C ++ eine Vielzahl von Möglichkeiten, Zeichenfolgen basierend auf beliebig komplexen Trennzeichen zu teilen, aber keine davon ist so gut gekapselt wie in anderen Sprachen. Die zahlreichen Möglichkeiten füllen ganze Blog-Beiträge .
Im einfachsten Fall können Sie mit iterieren, std::string::findbis Sie drücken std::string::npos, und den Inhalt mit extrahieren std::string::substr.
Eine flüssigere (und idiomatischere, aber einfachere) Version zum Aufteilen auf Leerzeichen würde Folgendes verwenden std::istringstream:
auto iss = std::istringstream{"The quick brown fox"};auto str = std::string{};while(iss >> str){
process(str);}
Mit std::istream_iterators kann der Inhalt des String-Streams auch mit seinem Iterator-Range-Konstruktor in einen Vektor kopiert werden.
Mehrere Bibliotheken (wie Boost.Tokenizer ) bieten bestimmte Tokenisierer.
Für eine erweiterte Aufteilung sind reguläre Ausdrücke erforderlich. C ++ bietet std::regex_token_iteratorzu diesem Zweck insbesondere Folgendes:
autoconst str ="The quick brown fox"s;autoconst re = std::regex{R"(\s+)"};autoconst vec = std::vector<std::string>(
std::sregex_token_iterator{begin(str), end(str), re,-1},
std::sregex_token_iterator{});
Leider ist Boost nicht immer für alle Projekte verfügbar. Ich muss nach einer Non-Boost-Antwort suchen.
FuzzyBunnySlippers
36
Nicht jedes Projekt ist offen für "Open Source". Ich arbeite in stark regulierten Branchen. Das ist wirklich kein Problem. Es ist nur eine Tatsache des Lebens. Boost ist nicht überall verfügbar.
FuzzyBunnySlippers
5
@NonlinearIdeas Bei der anderen Frage / Antwort ging es überhaupt nicht um Open Source-Projekte. Gleiches gilt für jedes Projekt. Das heißt, ich verstehe natürlich eingeschränkte Standards wie MISRA C, aber dann versteht es sich, dass Sie sowieso alles von Grund auf neu erstellen (es sei denn, Sie finden zufällig eine kompatible Bibliothek - eine Seltenheit). Auf jeden Fall geht es kaum darum, dass „Boost nicht verfügbar ist“ - Sie haben spezielle Anforderungen, für die fast jede allgemeine Antwort ungeeignet wäre.
Konrad Rudolph
1
@NonlinearIdeas In diesem Fall sind auch die anderen Nicht-Boost-Antworten nicht MISRA-konform.
Konrad Rudolph
3
@Dmitry Was ist "STL barf"?! Und die ganze Community ist sehr dafür, den C-Präprozessor zu ersetzen - tatsächlich gibt es Vorschläge, dies zu tun. Aber Ihr Vorschlag, stattdessen PHP oder eine andere Sprache zu verwenden, wäre ein großer Rückschritt.
Gutes Zeug, das habe ich kürzlich genutzt. Mein Visual Studio-Compiler hat einen merkwürdigen Fehler, bis ich die beiden Zeichen ">" vor dem Token-Bit (Text, Sep) durch ein Leerzeichen trenne: (Fehler C2947: Erwartet, dass '>' die Vorlagenargumentliste beendet, gefunden '> > ')
AndyUK
@AndyUK Ja, ohne den Speicherplatz analysiert der Compiler ihn als Extraktionsoperator und nicht als zwei schließende Vorlagen.
EnabrenTane
Theoretisch wurde das in C ++ 0x behoben
David Souther
3
Achten Sie auf die dritten Parameter des char_separatorKonstruktors ( drop_empty_tokensist die Standardeinstellung, Alternative ist keep_empty_tokens).
Benoit
5
@puk - Es ist ein häufig verwendetes Suffix für C ++ - Headerdateien. (wie .hfür C-Header)
Ferruccio
167
Hier ist eine ganz einfache:
#include<vector>#include<string>usingnamespace std;vector<string> split(constchar*str,char c =' '){vector<string> result;do{constchar*begin = str;while(*str != c &&*str)
str++;
result.push_back(string(begin, str));}while(0!=*str++);return result;}
Muss ich einen Prototyp für diese Methode in die .h-Datei einfügen?
Suhrob Samiev
5
Dies ist nicht gerade die "beste" Antwort, da immer noch ein Zeichenfolgenliteral verwendet wird, bei dem es sich um das einfache C-Konstantenzeichenarray handelt. Ich glaube, der Fragesteller hat gefragt, ob er eine C ++ - Zeichenfolge vom Typ "Zeichenfolge", die von dieser eingeführt wurde, tokenisieren könnte.
Vijay Kumar Kanta
Dies erfordert eine neue Antwort, da ich stark vermute, dass die Aufnahme regulärer Ausdrücke in C ++ 11 die beste Antwort geändert hat.
Omnifarious
113
Verwenden Sie strtok. Meiner Meinung nach ist es nicht erforderlich, eine Klasse für das Tokenisieren zu erstellen, es sei denn, strtok bietet Ihnen nicht das, was Sie benötigen. Möglicherweise nicht, aber in mehr als 15 Jahren, in denen ich verschiedene Parsing-Codes in C und C ++ geschrieben habe, habe ich immer strtok verwendet. Hier ist ein Beispiel
char myString[]="The quick brown fox";char*p = strtok(myString," ");while(p){
printf ("Token: %s\n", p);
p = strtok(NULL," ");}
Ein paar Vorsichtsmaßnahmen (die möglicherweise nicht Ihren Anforderungen entsprechen). Die Zeichenfolge wird dabei "zerstört", was bedeutet, dass EOS-Zeichen in den Begrenzungspunkten inline platziert werden. Für eine korrekte Verwendung müssen Sie möglicherweise eine nicht konstante Version der Zeichenfolge erstellen. Sie können auch die Liste der Trennzeichen während der Analyse ändern.
Meiner Meinung nach ist der obige Code viel einfacher und benutzerfreundlicher als das Schreiben einer separaten Klasse dafür. Für mich ist dies eine der Funktionen, die die Sprache bietet und die sie gut und sauber macht. Es ist einfach eine "C-basierte" Lösung. Es ist angemessen, es ist einfach und Sie müssen nicht viel zusätzlichen Code schreiben :-)
Nicht, dass ich C nicht mag, aber strtok ist nicht threadsicher, und Sie müssen sicher sein, dass die Zeichenfolge, die Sie senden, ein Nullzeichen enthält, um einen möglichen Pufferüberlauf zu vermeiden.
Tloach
11
Es gibt strtok_r, aber dies war eine C ++ - Frage.
Prof. Falken Vertrag verletzt
3
@tloach: In MS C ++ Compiler ist strtok threadsicher, da die interne statische Variable auf dem TLS (Thread Local Storage) erstellt wird (tatsächlich ist es vom Compiler abhängig)
Ahmed Said
3
@ahmed: thread safe bedeutet mehr als nur die Funktion zweimal in verschiedenen Threads ausführen zu können. In diesem Fall ist es möglich, dass der String während des gesamten Strtok-Laufs gültig ist, wenn der Thread während der Ausführung von strtok geändert wird. Strtok wird jedoch weiterhin durcheinander gebracht, da sich der String geändert hat. Er hat jetzt bereits das Nullzeichen überschritten und wird dies auch tun Lesen Sie den Speicher so lange weiter, bis entweder eine Sicherheitsverletzung auftritt oder ein Nullzeichen gefunden wird. Dies ist ein Problem mit den ursprünglichen C-String-Funktionen, wenn Sie an keiner Stelle eine Länge angeben, auf die Sie stoßen.
Tloach
4
Für strtok ist ein Zeiger auf ein nicht-const-nullterminiertes char-Array erforderlich, was in C ++ - Code nicht häufig vorkommt. Wie konvertieren Sie am liebsten aus einem std :: string in dieses Array?
FuzzyTew
105
Ein weiterer schneller Weg ist die Verwendung getline. Etwas wie:
Ich hatte Probleme bei der Verwendung dieser Technik mit 0x0A-Zeichen in der Zeichenfolge, wodurch die while-Schleife vorzeitig beendet wurde. Ansonsten ist es eine schöne einfache und schnelle Lösung.
Ryan H.
4
Dies ist gut, aber Sie müssen nur berücksichtigen, dass das Standardtrennzeichen '\ n' dabei nicht berücksichtigt wird. Dieses Beispiel wird funktionieren, aber wenn Sie etwas wie: while (getline (inFile, word, '')) verwenden, wobei inFile ein ifstream-Objekt ist, das mehrere Zeilen enthält, erhalten Sie lustige Ergebnisse.
hackrock
Es ist schade, dass getline den Stream und nicht den String zurückgibt, was ihn in Initialisierungslisten ohne temporären Speicher unbrauchbar macht
fuzzyTew
1
Cool! Kein Boost und C ++ 11, eine gute Lösung für die alten Projekte da draußen!
Deqing
1
DAS ist die Antwort, der Name der Funktion ist nur ein bisschen umständlich.
Nils
82
Sie können Streams, Iteratoren und den Kopieralgorithmus verwenden, um dies ziemlich direkt zu tun.
#include<string>#include<vector>#include<iostream>#include<istream>#include<ostream>#include<iterator>#include<sstream>#include<algorithm>int main(){
std::string str ="The quick brown fox";// construct a stream from the string
std::stringstream strstr(str);// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);}
Ich finde diese std :: irritierend zu lesen .. warum nicht "using" verwenden?
user35978
80
@Vadi: weil das Bearbeiten des Posts eines anderen ziemlich aufdringlich ist. @pheze: Ich ziehe es vor, auf stddiese Weise zu wissen, woher mein Objekt kommt, das ist nur eine Frage des Stils.
Matthieu M.
7
Ich verstehe Ihren Grund und ich denke, es ist tatsächlich eine gute Wahl, wenn es für Sie funktioniert, aber aus pädagogischer Sicht stimme ich Pheze tatsächlich zu. Es ist einfacher, ein völlig fremdes Beispiel wie dieses mit einem "using namespace std" oben zu lesen und zu verstehen, da die Interpretation der folgenden Zeilen weniger Aufwand erfordert ... insbesondere in diesem Fall, weil alles aus der Standardbibliothek stammt. Sie können das Lesen und Erkennen der Herkunft der Objekte durch eine Reihe von "using std :: string;" etc. Zumal die Funktion so kurz ist.
Cheshirekow
61
Obwohl die Präfixe "std ::" irritierend oder hässlich sind, ist es am besten, sie in Beispielcode aufzunehmen, damit völlig klar ist, woher diese Funktionen kommen. Wenn sie Sie stören, ist es trivial, sie durch ein "using" zu ersetzen, nachdem Sie das Beispiel gestohlen und als Ihr eigenes beansprucht haben.
Dlchambers
20
ja! was er sagte! Best Practices ist die Verwendung des Standardpräfixes. Jede große Codebasis wird zweifellos über eigene Bibliotheken und Namespaces verfügen. Die Verwendung von "using namespace std" bereitet Ihnen Kopfschmerzen, wenn Sie anfangen, Namespace-Konflikte zu verursachen.
Miek
48
Keine Beleidigung, aber für ein so einfaches Problem machen Sie die Dinge viel zu kompliziert. Es gibt viele Gründe, Boost zu verwenden . Aber für etwas so Einfaches ist es, als würde man eine Fliege mit einem 20 # Schlitten schlagen.
void
split(vector<string>& theStringVector,/* Altered/returned value */const string & theString,const string & theDelimiter){
UASSERT( theDelimiter.size(),>,0);// My own ASSERT macro.size_t start =0, end =0;while( end != string::npos){
end = theString.find( theDelimiter, start);// If at end, use length=maxLength. Else use length=end-start.
theStringVector.push_back( theString.substr( start,(end == string::npos)? string::npos : end - start));// If at end, use start=maxSize. Else use start=end+delimiter.
start =(( end >(string::npos - theDelimiter.size()))? string::npos : end + theDelimiter.size());}}
Zum Beispiel (für Dougs Fall),
#define SHOW(I,X) cout <<"["<<(I)<<"]\t "# X " = \"" << (X) << "\"" << endlint
main(){vector<string> v;
split( v,"A:PEP:909:Inventory Item",":");for(unsignedint i =0; i < v.size(); i++)
SHOW( i, v[i]);}
Und ja, wir hätten split () einen neuen Vektor zurückgeben können, anstatt einen zu übergeben. Es ist trivial, zu wickeln und zu überladen. Aber je nachdem, was ich mache, finde ich es oft besser, bereits vorhandene Objekte wiederzuverwenden, als immer neue zu erstellen. (Nur solange ich nicht vergesse, den Vektor dazwischen zu leeren!)
Warum ein Makro definieren, das Sie nur an einer Stelle verwenden? Und wie ist Ihr UASSERT besser als der Standard? Wenn Sie den Vergleich in 3 Token aufteilen, benötigen Sie lediglich mehr Kommas, als Sie sonst benötigen würden.
Crelbor
1
Vielleicht zeigt das UASSERT-Makro (in der Fehlermeldung) die tatsächliche Beziehung zwischen (und den Werten) der beiden verglichenen Werte? Das ist eigentlich eine ziemlich gute Idee, IMHO.
GhassanPL
10
Ugh, warum enthält die std::stringKlasse keine split () -Funktion?
Herr Shickadance
Ich denke, die letzte Zeile in der while-Schleife sollte sein start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());und die while-Schleife sollte sein while (start != string::npos). Außerdem überprüfe ich den Teilstring, um sicherzustellen, dass er nicht leer ist, bevor ich ihn in den Vektor einfüge.
John K
@JohnK Wenn die Eingabe zwei aufeinanderfolgende Trennzeichen hat, ist die Zeichenfolge zwischen ihnen eindeutig leer und sollte in den Vektor eingefügt werden. Wenn leere Werte für einen bestimmten Zweck nicht akzeptabel sind, ist dies eine andere Sache, aber meiner Meinung nach sollten solche Einschränkungen außerhalb dieser Art von sehr allgemeinen Funktionen durchgesetzt werden.
Lauri Nurmi
45
Eine Lösung mit regex_token_iterators:
#include<iostream>#include<regex>#include<string>usingnamespace std;int main(){
string str("The quick brown fox");
regex reg("\\s+");
sregex_token_iterator iter(str.begin(), str.end(), reg,-1);
sregex_token_iterator end;vector<string> vec(iter, end);for(auto a : vec){
cout << a << endl;}}
Dies sollte die am besten bewertete Antwort sein. Dies ist der richtige Weg, um dies in C ++> = 11 zu tun.
Omnifarious
1
Ich bin froh, dass ich bis zu dieser Antwort gescrollt habe (derzeit nur 9 positive Stimmen). Genau so sollte ein C ++ 11-Code für diese Aufgabe aussehen!
YePhIcK
Ausgezeichnete Antwort, die nicht auf externen Bibliotheken beruht und bereits verfügbare Bibliotheken verwendet
Andrew
1
Tolle Antwort, die die größte Flexibilität bei Trennzeichen bietet. Einige Einschränkungen: Durch die Verwendung von \ s + Regex werden leere Token in der Mitte des Textes vermieden, es wird jedoch ein leeres erstes Token ausgegeben, wenn der Text mit Leerzeichen beginnt. Außerdem scheint Regex langsam zu sein: Auf meinem Laptop dauert es für 20 MB zufälligen Text 0,6 Sekunden, verglichen mit 0,014 Sekunden für strtok, strsep oder Parhams Antwort mit str.find_first_of oder 0,027 Sekunden für Perl oder 0,021 Sekunden für Python . Bei kurzen Texten spielt die Geschwindigkeit möglicherweise keine Rolle.
Mark Gates
2
Ok, vielleicht sieht es cool aus, aber dies ist eindeutig eine Überbeanspruchung regulärer Ausdrücke. Nur dann sinnvoll, wenn Sie sich nicht für die Leistung interessieren.
#include<vector>#include<boost/algorithm/string.hpp>int main(){auto s ="a,b, c ,,e,f,";
std::vector<std::string> fields;
boost::split(fields, s, boost::is_any_of(","));for(constauto& field : fields)
std::cout <<"\""<< field <<"\"\n";return0;}
Dies ist eine einfache STL-only - Lösung (~ 5 Zeilen!) Mit std::findund std::find_first_not_ofdass Griffe Wiederholungen des Trennzeichens (wie Leerzeichen oder Punkte zum Beispiel) sowie vorderen und hinteren Begrenzungszeichen:
#include<string>#include<vector>void tokenize(std::string str, std::vector<string>&token_v){size_t start = str.find_first_not_of(DELIMITER), end=start;while(start != std::string::npos){// Find next occurence of delimiter
end = str.find(DELIMITER, start);// Push back the token found into vector
token_v.push_back(str.substr(start, end-start));// Skip all occurences of the delimiter to find new start
start = str.find_first_not_of(DELIMITER, end);}}
Dies ist gut, aber ich denke, Sie müssen find_first_of () anstelle von find () verwenden, damit dies ordnungsgemäß mit mehreren Trennzeichen funktioniert.
2
@ user755921 Mehrere Trennzeichen werden übersprungen, wenn die Startposition mit find_first_not_of gefunden wird.
Anfänger
16
pystring ist eine kleine Bibliothek, die eine Reihe von Python-Zeichenfolgenfunktionen implementiert, einschließlich der Split-Methode:
#include<string>#include<vector>#include"pystring.h"
std::vector<std::string> chunks;
pystring::split("this string", chunks);// also can specify a separator
pystring::split("this-string", chunks,"-");
Wow, du hast meine unmittelbare Frage und viele zukünftige Fragen beantwortet. Ich verstehe, dass C ++ mächtig ist. Wenn das Teilen eines Strings jedoch zu Quellcode wie den obigen Antworten führt, ist dies eindeutig entmutigend. Ich würde gerne andere Bibliotheken wie diese kennenlernen, die die Bequemlichkeiten höherer Sprachen nach unten ziehen.
Ross
wow, du hast meinen Tag ernsthaft gemacht !! wusste nichts über Pystring. Das spart mir viel Zeit!
Accraze
11
Ich habe diese Antwort für eine ähnliche Frage gepostet.
Das Rad nicht neu erfinden. Ich habe eine Reihe von Bibliotheken verwendet und die schnellste und flexibelste, die mir begegnet ist, ist: C ++ String Toolkit Library .
Hier ist ein Beispiel für die Verwendung, das ich an anderer Stelle im Stackoverflow gepostet habe.
#include<iostream>#include<vector>#include<string>#include<strtk.hpp>constchar*whitespace =" \t\r\n\f";constchar*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;}}return0;}
Überprüfen Sie dieses Beispiel. Es könnte Ihnen helfen ..
#include<iostream>#include<sstream>usingnamespace std;int main (){
string tmps;
istringstream is ("the dellimiter is the space");while(is.good ()){
is >> tmps;
cout << tmps <<"\n";}return0;}
Diese Tokenize () -Funktion überspringt leere Token. Wenn beispielsweise die Teilzeichenfolge "%%" in der Hauptzeichenfolge enthalten ist, wird kein leeres Token zurückgegeben. Es wird übersprungen.
Sheen
4
Wenn Sie bereit sind, C zu verwenden, können Sie die strtok- Funktion verwenden. Sie sollten bei der Verwendung auf Multithreading-Probleme achten.
Beachten Sie, dass strtok die Zeichenfolge, die Sie überprüfen, ändert, sodass Sie sie nicht für const char * -Strings verwenden können, ohne eine Kopie zu erstellen.
Graeme Perrow
9
Das Multithreading-Problem besteht darin, dass strtok eine globale Variable verwendet, um zu verfolgen, wo es sich befindet. Wenn Sie also zwei Threads haben, die jeweils strtok verwenden, erhalten Sie ein undefiniertes Verhalten.
JohnMcG
@JohnMcG Oder verwenden Sie einfach, strtok_swas im Grunde strtokmit expliziter Statusübergabe ist .
Matthias
4
Für einfache Sachen benutze ich einfach folgendes:
Feiger Haftungsausschluss: Ich schreibe eine Echtzeit-Datenverarbeitungssoftware, bei der die Daten über Binärdateien, Sockets oder einen API-Aufruf (E / A-Karten, Kameras) eingehen. Ich verwende diese Funktion niemals für etwas Komplizierteres oder Zeitkritischeres als das Lesen externer Konfigurationsdateien beim Start.
+1 für den Vorschlag von Regex. Wenn Sie keine Warp-Geschwindigkeit benötigen, ist dies die flexibelste Lösung, die noch nicht überall unterstützt wird. Mit der Zeit wird dies jedoch weniger wichtig.
Odinthenerd
+1 von mir, habe gerade <regex> in c ++ 11 ausprobiert. So einfach und elegant
StahlRat
4
Viele zu komplizierte Vorschläge hier. Probieren Sie diese einfache std :: string-Lösung aus:
Wenn Sie die Komplexität mithilfe der Standardfunktionalität abstrahieren möchten, wie On Freund vorschlägt,strtok ist dies eine einfache Option:
vector<string> tokens;for(auto i = strtok(data(str)," "); i !=nullptr; i = strtok(nullptr," ")) tokens.push_back(i);
Wenn Sie keinen Zugriff auf C ++ 17 haben, müssen Sie data(str)wie in diesem Beispiel ersetzen : http://ideone.com/8kAGoa
Obwohl im Beispiel nicht gezeigt, strtokmuss nicht für jedes Token das gleiche Trennzeichen verwendet werden. Zusammen mit diesem Vorteil gibt es jedoch mehrere Nachteile:
strtokkann nicht für mehrere stringsgleichzeitig verwendet werden: Entweder nullptrmuss a übergeben werden, um die aktuelle Tokenisierung fortzusetzenstring oder es muss eine neuechar* übergeben werden (es gibt jedoch einige nicht standardmäßige Implementierungen, die dies unterstützen, wie z.strtok_s )
Aus dem gleichen Grunde strtok kann nicht für mehrere Threads gleichzeitig verwendet werden (dies kann jedoch eine definierte Implementierung sein, zum Beispiel: Die Implementierung von Visual Studio ist threadsicher ).
Das Aufrufen strtokändert die Funktion, mit der stringes ausgeführt wird, sodass es nicht für const strings-, const char*s- oder Literal-Zeichenfolgen verwendet werden kann, um eine dieser Zeichenfolgen mit einem Token zu versehen strtokoder zu bearbeiten string, dessen Inhalt beibehalten werden muss. Es strmüsste kopiert werden, dann könnte die Kopie kopiert werden operiert werden
Die vorherigen Methoden können kein direktes Token generieren vector, dh ohne sie in eine Hilfsfunktion zu abstrahieren, die sie nicht initialisieren können const vector<string> tokens. Diese Funktionalität und die Fähigkeit, jeden Leerraumbegrenzer zu akzeptieren , können mithilfe von a genutzt werden istream_iterator. Zum Beispiel gegeben: const string str{ "The quick \tbrown \nfox" }Wir können dies tun:
Die erforderliche Konstruktion einer istringstreamfür diese Option hat weitaus höhere Kosten als die beiden vorherigen Optionen, diese Kosten sind jedoch in der Regel in den Kosten für die stringZuweisung verborgen .
Wenn keine der oben genannten Optionen für Ihre Tokenisierungsanforderungen flexibel genug ist, ist die flexibelste Option die Verwendung einer regex_token_iteratornatürlich mit dieser Flexibilität verbundenen höheren Kosten, aber auch dies ist wahrscheinlich in den stringZuweisungskosten verborgen . Nehmen wir zum Beispiel an, wir möchten ein Token basierend auf nicht maskierten Kommas erstellen und dabei auch Leerzeichen essen, wenn const string str{ "The ,qu\\,ick ,\tbrown, fox" }wir folgende Eingaben machen: Wir können dies tun:
strtok_sist übrigens C11-Standard. strtok_rist ein POSIX2001-Standard. Zwischen diesen beiden gibt es strtokfür die meisten Plattformen eine Standard-Wiedereintrittsversion von .
Andon M. Coleman
@ AndonM.Coleman Dies ist jedoch eine C ++ - Frage, die in C ++ #include <cstring>nur die c99- Version von enthält strtok. Ich gehe also davon aus, dass Sie diesen Kommentar nur als unterstützendes Material bereitstellen, um die implementierungsspezifische Verfügbarkeit von strtokErweiterungen zu demonstrieren .
Jonathan Mee
1
Nur, dass es nicht so ungewöhnlich ist, wie die Leute sonst glauben könnten. strtok_swird sowohl von C11 als auch als eigenständige Erweiterung in der C-Laufzeit von Microsoft bereitgestellt. Es gibt hier ein merkwürdiges Stück Geschichte, in dem die _sFunktionen von Microsoft zum C-Standard wurden.
Andon M. Coleman
@ AndonM.Coleman Richtig, ich bin bei dir. Wenn es sich um den C11-Standard handelt, gelten für die Schnittstelle und die Implementierung Einschränkungen, die unabhängig von der Plattform ein identisches Verhalten erfordern. Jetzt besteht das einzige Problem darin, sicherzustellen, dass die C11-Funktion plattformübergreifend für uns verfügbar ist. Hoffentlich wird der C11-Standard von C ++ 17 oder C ++ 20 ausgewählt.
Jonathan Mee
3
Ich weiß, dass diese Frage bereits beantwortet ist, aber ich möchte einen Beitrag leisten. Vielleicht ist meine Lösung ein bisschen einfach, aber das habe ich mir ausgedacht:
Ich habe Ihre Lösung aus der Menge verwendet :) Kann ich Ihren Code ändern, um ein Trennzeichen hinzuzufügen?
Zac
1
@ Zac froh, dass es Ihnen gefallen hat und Sie können es ändern ... fügen Sie einfach einen fett gedruckten Update-Abschnitt zu meiner Antwort hinzu ...
NutCracker
2
Hier ist ein Ansatz, mit dem Sie steuern können, ob leere Token eingeschlossen (wie strsep) oder ausgeschlossen (wie strtok) sind.
#include<string.h>// for strchr and strlen/*
* want_empty_tokens==true : include empty tokens, like strsep()
* want_empty_tokens==false : exclude empty tokens, like strtok()
*/
std::vector<std::string> tokenize(constchar* src,char delim,bool want_empty_tokens){
std::vector<std::string> tokens;if(src and *src !='\0')// defensivewhile(true){constchar* d = strchr(src, delim);size_t len =(d)? d-src : strlen(src);if(len or want_empty_tokens)
tokens.push_back( std::string(src, len));// capture tokenif(d) src += len+1;elsebreak;}return tokens;}
Mir kommt es seltsam vor, dass bei uns allen geschwindigkeitsbewussten Nerds hier auf SO niemand eine Version vorgestellt hat, die eine nach der Kompilierungszeit generierte Nachschlagetabelle für das Trennzeichen verwendet (Beispielimplementierung weiter unten). Die Verwendung einer Nachschlagetabelle und von Iteratoren sollte std :: regex in ihrer Effizienz übertreffen. Wenn Sie Regex nicht schlagen müssen, verwenden Sie es einfach, seinen Standard ab C ++ 11 und super flexibel.
Einige haben bereits Regex vorgeschlagen, aber für die Noobs ist hier ein Beispiel, das genau das tun sollte, was das OP erwartet:
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};while(std::regex_search (it,end,m,e)){
ret.emplace_back(m.str());
std::advance(it, m.position()+ m.length());//next start position = match position + match length}return ret;}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){//comfort version calls flexible versionreturn split(s.cbegin(), s.cend(), std::move(e));}int main (){
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};auto v = split(str);for(constauto&s:v){
std::cout << s << std::endl;}
std::cout <<"crazy version:"<< std::endl;
v = split(str, std::regex{"[^e]+"});//using e as delim shows flexibilityfor(constauto&s:v){
std::cout << s << std::endl;}return0;}
Wenn wir schneller sein und die Einschränkung akzeptieren müssen, dass alle Zeichen 8 Bit groß sein müssen, können wir zur Kompilierungszeit mithilfe der Metaprogrammierung eine Nachschlagetabelle erstellen:
template<bool...>structBoolSequence{};//just here to hold boolstemplate<char...>structCharSequence{};//just here to hold charstemplate<typename T,char C>structContains;//generictemplate<charFirst,char...Cs,charMatch>//not first specializationstructContains<CharSequence<First,Cs...>,Match>:Contains<CharSequence<Cs...>,Match>{};//strip first and increase indextemplate<charFirst,char...Cs>//is first specializationstructContains<CharSequence<First,Cs...>,First>: std::true_type {};template<charMatch>//not found specializationstructContains<CharSequence<>,Match>: std::false_type{};template<int I,typename T,typename U>structMakeSequence;//generictemplate<int I,bool...Bs,typename U>structMakeSequence<I,BoolSequence<Bs...>, U>://not lastMakeSequence<I-1,BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};template<bool...Bs,typename U>structMakeSequence<0,BoolSequence<Bs...>,U>{//last usingType=BoolSequence<Bs...>;};template<typename T>structBoolASCIITable;template<bool...Bs>structBoolASCIITable<BoolSequence<Bs...>>{/* could be made constexpr but not yet supported by MSVC */staticbool isDelim(constchar c){staticconstbool table[256]={Bs...};return table[static_cast<int>(c)];}};usingDelims=CharSequence<'.',',',' ',':','\n'>;//list your custom delimiters hereusingTable=BoolASCIITable<typenameMakeSequence<256,BoolSequence<>,Delims>::Type>;
Damit getNextTokenist es einfach , eine Funktion zu erstellen:
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{}));//find first non delim or endauto second = std::find_if(begin,end,Table{});//find first delim or endreturn std::make_pair(begin,second);}
Die Verwendung ist auch einfach:
int main(){
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};auto it = std::begin(s);auto end = std::end(s);while(it != std::end(s)){auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second)<< std::endl;
it = token.second;}return0;}
Ist es möglich, mit einem String-Trennzeichen zu token;
Galigator
Diese Version ist nur für Einzelzeichen-Trennzeichen optimiert. Die Verwendung einer Nachschlagetabelle ist nicht für Trennzeichen mit mehreren Zeichen (Zeichenfolgen) geeignet, sodass die Effizienz von Regex schwerer zu übertreffen ist.
Odinthenerd
1
Sie können boost :: make_find_iterator nutzen. Ähnliches:
template<typename CH>inlinevector< basic_string<CH>> tokenize(const basic_string<CH>&Input,const basic_string<CH>&Delimiter,bool remove_empty_token
){typedeftypename basic_string<CH>::const_iteratorstring_iterator_t;typedef boost::find_iterator<string_iterator_t>string_find_iterator_t;vector< basic_string<CH>>Result;string_iterator_t it =Input.begin();string_iterator_t it_end =Input.end();for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i !=string_find_iterator_t();++i){if(remove_empty_token){if(it != i->begin())Result.push_back(basic_string<CH>(it,i->begin()));}elseResult.push_back(basic_string<CH>(it,i->begin()));
it = i->end();}if(it != it_end)Result.push_back(basic_string<CH>(it,it_end));returnResult;}
Hier ist mein Swiss® Army Knife von String-Tokenizern zum Aufteilen von Strings nach Leerzeichen, zum Berücksichtigen von Strings mit einfachen und doppelten Anführungszeichen sowie zum Entfernen dieser Zeichen aus den Ergebnissen. Ich habe RegexBuddy 4.x verwendet, um den größten Teil des Code-Snippets zu generieren , aber ich habe eine benutzerdefinierte Behandlung zum Entfernen von Anführungszeichen und ein paar anderen Dingen hinzugefügt.
(Abwärts-) Abstimmungen können genauso konstruktiv sein wie Aufwärtsstimmen, aber nicht, wenn Sie keine Kommentare dazu hinterlassen, warum ...
kayleeFrye_onDeck
1
Ich habe Sie ausgeglichen, aber es könnte sein, dass der Code für den Programmierer, der googelt, wie man einen String teilt, ziemlich entmutigend aussieht, insbesondere ohne Dokumentation
mattshu
Danke @mattshu! Sind es die Regex-Segmente, die es entmutigend machen oder etwas anderes?
kayleeFrye_onDeck
0
Wenn die maximale Länge der zu tokenisierenden Eingabezeichenfolge bekannt ist, kann dies ausgenutzt und eine sehr schnelle Version implementiert werden. Ich skizziere die Grundidee unten, die sowohl von strtok () als auch von der Datenstruktur "Suffix Array" inspiriert wurde, die Jon Bentleys 2. Ausgabe von "Programming Perls", Kapitel 15, beschreibt. Die C ++ - Klasse bietet in diesem Fall nur eine gewisse Organisation und Bequemlichkeit von Nutzen. Die gezeigte Implementierung kann leicht erweitert werden, um führende und nachfolgende Leerzeichen in den Token zu entfernen.
Grundsätzlich kann man die Trennzeichen durch Zeichenfolgen-terminierende '\ 0'-Zeichen ersetzen und Zeiger auf die Token mit der geänderten Zeichenfolge setzen. Im Extremfall, wenn die Zeichenfolge nur aus Trennzeichen besteht, erhält man die Zeichenfolgenlänge plus 1 resultierende leere Token. Es ist praktisch, die zu ändernde Zeichenfolge zu duplizieren.
Header-Datei:
classTextLineSplitter{public:TextLineSplitter(constsize_t max_line_len );~TextLineSplitter();voidSplitLine(constchar*line,constchar sep_char =',',);inlinesize_tNumTokens(void)const{return mNumTokens;}constchar*GetToken(constsize_t token_idx )const{
assert( token_idx < mNumTokens );return mTokens[ token_idx ];}private:constsize_t mStorageSize;char*mBuff;char**mTokens;size_t mNumTokens;inlinevoidResetContent(void){
memset( mBuff,0, mStorageSize );// mark all items as empty:
memset( mTokens,0, mStorageSize *sizeof(char*));// reset counter for found items:
mNumTokens =0L;}};
// create an instance capable of splitting strings up to 1000 chars long:TextLineSplitter spl(1000);
spl.SplitLine("Item1,,Item2,Item3");for(size_t i =0; i < spl.NumTokens(); i++){
printf("%s\n", spl.GetToken( i ));}
boost::tokenizerist Ihr Freund, aber erwägen Sie, Ihren Code in Bezug auf Internationalisierungsprobleme (i18n) portabel zu machen, indem Sie wstring/ wchar_tanstelle der Legacy string/ char-Typen verwenden.
#include<iostream>#include<boost/tokenizer.hpp>#include<string>usingnamespace std;usingnamespace boost;typedef tokenizer<char_separator<wchar_t>,
wstring::const_iterator, wstring>Tok;int main(){
wstring s;while(getline(wcin, s)){
char_separator<wchar_t> sep(L" ");// list of separator charactersTok tok(s, sep);for(Tok::iterator beg = tok.begin(); beg != tok.end();++beg){
wcout <<*beg << L"\t";// output (or store in vector)}
wcout << L"\n";}return0;}
"Legacy" ist definitiv nicht korrekt und wchar_tein schrecklicher implementierungsabhängiger Typ, den niemand verwenden sollte, es sei denn, dies ist absolut notwendig.
Kaffee und Code
Die Verwendung von wchar_t löst keine i18n-Probleme automatisch. Sie verwenden Codierungen, um dieses Problem zu lösen. Wenn Sie eine Zeichenfolge durch ein Trennzeichen teilen, bedeutet dies, dass das Trennzeichen nicht mit dem codierten Inhalt eines Tokens in der Zeichenfolge kollidiert. Möglicherweise ist ein Entkommen erforderlich usw. wchar_t ist keine magische Lösung dafür.
Yonil
0
Einfacher C ++ - Code (Standard C ++ 98) akzeptiert mehrere Trennzeichen (angegeben in einem std :: string) und verwendet nur Vektoren, Strings und Iteratoren.
#include<iostream>#include<vector>#include<string>#include<stdexcept>
std::vector<std::string>
split(const std::string& str,const std::string& delim){
std::vector<std::string> result;if(str.empty())throw std::runtime_error("Can not tokenize an empty string!");
std::string::const_iterator begin, str_it;
begin = str_it = str.begin();do{while(delim.find(*str_it)== std::string::npos && str_it != str.end())
str_it++;// find the position of the first delimiter in str
std::string token = std::string(begin, str_it);// grab the tokenif(!token.empty())// empty token only when str starts with a delimiter
result.push_back(token);// push the token into a vector<string>while(delim.find(*str_it)!= std::string::npos && str_it != str.end())
str_it++;// ignore the additional consecutive delimiters
begin = str_it;// process the remaining tokens}while(str_it != str.end());return result;}int main(){
std::string test_string =".this is.a.../.simple;;test;;;END";
std::string delim ="; ./";// string containing the delimiters
std::vector<std::string> tokens = split(test_string, delim);for(std::vector<std::string>::const_iterator it = tokens.begin();
it != tokens.end(); it++)
std::cout <<*it << std::endl;}
Antworten:
C ++ - Standardbibliotheksalgorithmen basieren ziemlich universell auf Iteratoren und nicht auf konkreten Containern. Leider ist es schwierig, eine Java-ähnliche
split
Funktion in der C ++ - Standardbibliothek bereitzustellen , obwohl niemand argumentiert, dass dies praktisch wäre. Aber wie würde die Rückgabe aussehen?std::vector<std::basic_string<…>>
? Vielleicht, aber dann sind wir gezwungen, (möglicherweise redundante und kostspielige) Zuweisungen durchzuführen.Stattdessen bietet C ++ eine Vielzahl von Möglichkeiten, Zeichenfolgen basierend auf beliebig komplexen Trennzeichen zu teilen, aber keine davon ist so gut gekapselt wie in anderen Sprachen. Die zahlreichen Möglichkeiten füllen ganze Blog-Beiträge .
Im einfachsten Fall können Sie mit iterieren,
std::string::find
bis Sie drückenstd::string::npos
, und den Inhalt mit extrahierenstd::string::substr
.Eine flüssigere (und idiomatischere, aber einfachere) Version zum Aufteilen auf Leerzeichen würde Folgendes verwenden
std::istringstream
:Mit
std::istream_iterator
s kann der Inhalt des String-Streams auch mit seinem Iterator-Range-Konstruktor in einen Vektor kopiert werden.Mehrere Bibliotheken (wie Boost.Tokenizer ) bieten bestimmte Tokenisierer.
Für eine erweiterte Aufteilung sind reguläre Ausdrücke erforderlich. C ++ bietet
std::regex_token_iterator
zu diesem Zweck insbesondere Folgendes:quelle
Die Boost-Tokenizer- Klasse kann so etwas ganz einfach machen:
Aktualisiert für C ++ 11:
quelle
char_separator
Konstruktors (drop_empty_tokens
ist die Standardeinstellung, Alternative istkeep_empty_tokens
)..h
für C-Header)Hier ist eine ganz einfache:
quelle
Verwenden Sie strtok. Meiner Meinung nach ist es nicht erforderlich, eine Klasse für das Tokenisieren zu erstellen, es sei denn, strtok bietet Ihnen nicht das, was Sie benötigen. Möglicherweise nicht, aber in mehr als 15 Jahren, in denen ich verschiedene Parsing-Codes in C und C ++ geschrieben habe, habe ich immer strtok verwendet. Hier ist ein Beispiel
Ein paar Vorsichtsmaßnahmen (die möglicherweise nicht Ihren Anforderungen entsprechen). Die Zeichenfolge wird dabei "zerstört", was bedeutet, dass EOS-Zeichen in den Begrenzungspunkten inline platziert werden. Für eine korrekte Verwendung müssen Sie möglicherweise eine nicht konstante Version der Zeichenfolge erstellen. Sie können auch die Liste der Trennzeichen während der Analyse ändern.
Meiner Meinung nach ist der obige Code viel einfacher und benutzerfreundlicher als das Schreiben einer separaten Klasse dafür. Für mich ist dies eine der Funktionen, die die Sprache bietet und die sie gut und sauber macht. Es ist einfach eine "C-basierte" Lösung. Es ist angemessen, es ist einfach und Sie müssen nicht viel zusätzlichen Code schreiben :-)
quelle
Ein weiterer schneller Weg ist die Verwendung
getline
. Etwas wie:Wenn Sie möchten, können Sie eine einfache
split()
Methode erstellenvector<string>
, die a zurückgibt, was sehr nützlich ist.quelle
Sie können Streams, Iteratoren und den Kopieralgorithmus verwenden, um dies ziemlich direkt zu tun.
quelle
std
diese Weise zu wissen, woher mein Objekt kommt, das ist nur eine Frage des Stils.Keine Beleidigung, aber für ein so einfaches Problem machen Sie die Dinge viel zu kompliziert. Es gibt viele Gründe, Boost zu verwenden . Aber für etwas so Einfaches ist es, als würde man eine Fliege mit einem 20 # Schlitten schlagen.
Zum Beispiel (für Dougs Fall),
Und ja, wir hätten split () einen neuen Vektor zurückgeben können, anstatt einen zu übergeben. Es ist trivial, zu wickeln und zu überladen. Aber je nachdem, was ich mache, finde ich es oft besser, bereits vorhandene Objekte wiederzuverwenden, als immer neue zu erstellen. (Nur solange ich nicht vergesse, den Vektor dazwischen zu leeren!)
Referenz: http://www.cplusplus.com/reference/string/string/ .
(Ich habe ursprünglich eine Antwort auf Dougs Frage geschrieben: Ändern und Extrahieren von C ++ - Zeichenfolgen basierend auf Trennzeichen (geschlossen) . Aber da Martin York diese Frage hier mit einem Zeiger geschlossen hat ... werde ich nur meinen Code verallgemeinern.)
quelle
std::string
Klasse keine split () -Funktion?start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());
und die while-Schleife sollte seinwhile (start != string::npos)
. Außerdem überprüfe ich den Teilstring, um sicherzustellen, dass er nicht leer ist, bevor ich ihn in den Vektor einfüge.Eine Lösung mit
regex_token_iterator
s:quelle
Boost hat eine starke Split-Funktion: boost :: algorithm :: split .
Beispielprogramm:
Ausgabe:
quelle
Ich weiß, dass Sie nach einer C ++ - Lösung gefragt haben, aber Sie könnten dies als hilfreich erachten:
Qt
Der Vorteil gegenüber Boost in diesem Beispiel besteht darin, dass es sich um eine direkte Eins-zu-Eins-Zuordnung zum Code Ihres Posts handelt.
Weitere Informationen finden Sie in der Qt-Dokumentation
quelle
Hier ist eine Beispiel-Tokenizer-Klasse, die möglicherweise das tut, was Sie wollen
Beispiel:
quelle
Dies ist eine einfache STL-only - Lösung (~ 5 Zeilen!) Mit
std::find
undstd::find_first_not_of
dass Griffe Wiederholungen des Trennzeichens (wie Leerzeichen oder Punkte zum Beispiel) sowie vorderen und hinteren Begrenzungszeichen:Probieren Sie es live aus !
quelle
pystring ist eine kleine Bibliothek, die eine Reihe von Python-Zeichenfolgenfunktionen implementiert, einschließlich der Split-Methode:
quelle
Ich habe diese Antwort für eine ähnliche Frage gepostet.
Das Rad nicht neu erfinden. Ich habe eine Reihe von Bibliotheken verwendet und die schnellste und flexibelste, die mir begegnet ist, ist: C ++ String Toolkit Library .
Hier ist ein Beispiel für die Verwendung, das ich an anderer Stelle im Stackoverflow gepostet habe.
quelle
Überprüfen Sie dieses Beispiel. Es könnte Ihnen helfen ..
quelle
while ( is >> tmps ) { std::cout << tmps << "\n"; }
MFC / ATL hat einen sehr schönen Tokenizer. Von MSDN:
quelle
Wenn Sie bereit sind, C zu verwenden, können Sie die strtok- Funktion verwenden. Sie sollten bei der Verwendung auf Multithreading-Probleme achten.
quelle
strtok_s
was im Grundestrtok
mit expliziter Statusübergabe ist .Für einfache Sachen benutze ich einfach folgendes:
Feiger Haftungsausschluss: Ich schreibe eine Echtzeit-Datenverarbeitungssoftware, bei der die Daten über Binärdateien, Sockets oder einen API-Aufruf (E / A-Karten, Kameras) eingehen. Ich verwende diese Funktion niemals für etwas Komplizierteres oder Zeitkritischeres als das Lesen externer Konfigurationsdateien beim Start.
quelle
Sie können einfach eine Bibliothek für reguläre Ausdrücke verwenden und diese mit regulären Ausdrücken lösen.
Verwenden Sie den Ausdruck (\ w +) und die Variable in \ 1 (oder $ 1, abhängig von der Bibliotheksimplementierung regulärer Ausdrücke).
quelle
Viele zu komplizierte Vorschläge hier. Probieren Sie diese einfache std :: string-Lösung aus:
quelle
Ich dachte, dafür war der
>>
Operator für String-Streams gedacht :quelle
Die Antwort von Adam Pierce liefert einen handgesponnenen Tokenizer, der a aufnimmt
const char*
. Es ist etwas problematischer mit Iteratoren zu tun, da das Inkrementieren des Enditerators einesstring
undefiniert ist . Das heißt, vorausgesetzt,string str{ "The quick brown fox" }
wir können dies sicherlich erreichen:Live Example
Wenn Sie die Komplexität mithilfe der Standardfunktionalität abstrahieren möchten, wie On Freund vorschlägt,
strtok
ist dies eine einfache Option:Wenn Sie keinen Zugriff auf C ++ 17 haben, müssen Sie
data(str)
wie in diesem Beispiel ersetzen : http://ideone.com/8kAGoaObwohl im Beispiel nicht gezeigt,
strtok
muss nicht für jedes Token das gleiche Trennzeichen verwendet werden. Zusammen mit diesem Vorteil gibt es jedoch mehrere Nachteile:strtok
kann nicht für mehrerestrings
gleichzeitig verwendet werden: Entwedernullptr
muss a übergeben werden, um die aktuelle Tokenisierung fortzusetzenstring
oder es muss eine neuechar*
übergeben werden (es gibt jedoch einige nicht standardmäßige Implementierungen, die dies unterstützen, wie z.strtok_s
)strtok
kann nicht für mehrere Threads gleichzeitig verwendet werden (dies kann jedoch eine definierte Implementierung sein, zum Beispiel: Die Implementierung von Visual Studio ist threadsicher ).strtok
ändert die Funktion, mit derstring
es ausgeführt wird, sodass es nicht fürconst string
s-,const char*
s- oder Literal-Zeichenfolgen verwendet werden kann, um eine dieser Zeichenfolgen mit einem Token zu versehenstrtok
oder zu bearbeitenstring
, dessen Inhalt beibehalten werden muss. Esstr
müsste kopiert werden, dann könnte die Kopie kopiert werden operiert werdenc ++ 20bietet uns die Möglichkeit,
split_view
Zeichenfolgen zerstörungsfrei zu tokenisieren: https://topanswers.xyz/cplusplus?q=749#a874Die vorherigen Methoden können kein direktes Token generieren
vector
, dh ohne sie in eine Hilfsfunktion zu abstrahieren, die sie nicht initialisieren könnenconst vector<string> tokens
. Diese Funktionalität und die Fähigkeit, jeden Leerraumbegrenzer zu akzeptieren , können mithilfe von a genutzt werdenistream_iterator
. Zum Beispiel gegeben:const string str{ "The quick \tbrown \nfox" }
Wir können dies tun:Live Example
Die erforderliche Konstruktion einer
istringstream
für diese Option hat weitaus höhere Kosten als die beiden vorherigen Optionen, diese Kosten sind jedoch in der Regel in den Kosten für diestring
Zuweisung verborgen .Wenn keine der oben genannten Optionen für Ihre Tokenisierungsanforderungen flexibel genug ist, ist die flexibelste Option die Verwendung einer
regex_token_iterator
natürlich mit dieser Flexibilität verbundenen höheren Kosten, aber auch dies ist wahrscheinlich in denstring
Zuweisungskosten verborgen . Nehmen wir zum Beispiel an, wir möchten ein Token basierend auf nicht maskierten Kommas erstellen und dabei auch Leerzeichen essen, wennconst string str{ "The ,qu\\,ick ,\tbrown, fox" }
wir folgende Eingaben machen: Wir können dies tun:Live Example
quelle
strtok_s
ist übrigens C11-Standard.strtok_r
ist ein POSIX2001-Standard. Zwischen diesen beiden gibt esstrtok
für die meisten Plattformen eine Standard-Wiedereintrittsversion von .#include <cstring>
nur die c99- Version von enthältstrtok
. Ich gehe also davon aus, dass Sie diesen Kommentar nur als unterstützendes Material bereitstellen, um die implementierungsspezifische Verfügbarkeit vonstrtok
Erweiterungen zu demonstrieren .strtok_s
wird sowohl von C11 als auch als eigenständige Erweiterung in der C-Laufzeit von Microsoft bereitgestellt. Es gibt hier ein merkwürdiges Stück Geschichte, in dem die_s
Funktionen von Microsoft zum C-Standard wurden.Ich weiß, dass diese Frage bereits beantwortet ist, aber ich möchte einen Beitrag leisten. Vielleicht ist meine Lösung ein bisschen einfach, aber das habe ich mir ausgedacht:
Bitte kommentieren Sie, ob es einen besseren Ansatz für etwas in meinem Code gibt oder ob etwas nicht stimmt.
UPDATE: generisches Trennzeichen hinzugefügt
quelle
Hier ist ein Ansatz, mit dem Sie steuern können, ob leere Token eingeschlossen (wie strsep) oder ausgeschlossen (wie strtok) sind.
quelle
Mir kommt es seltsam vor, dass bei uns allen geschwindigkeitsbewussten Nerds hier auf SO niemand eine Version vorgestellt hat, die eine nach der Kompilierungszeit generierte Nachschlagetabelle für das Trennzeichen verwendet (Beispielimplementierung weiter unten). Die Verwendung einer Nachschlagetabelle und von Iteratoren sollte std :: regex in ihrer Effizienz übertreffen. Wenn Sie Regex nicht schlagen müssen, verwenden Sie es einfach, seinen Standard ab C ++ 11 und super flexibel.
Einige haben bereits Regex vorgeschlagen, aber für die Noobs ist hier ein Beispiel, das genau das tun sollte, was das OP erwartet:
Wenn wir schneller sein und die Einschränkung akzeptieren müssen, dass alle Zeichen 8 Bit groß sein müssen, können wir zur Kompilierungszeit mithilfe der Metaprogrammierung eine Nachschlagetabelle erstellen:
Damit
getNextToken
ist es einfach , eine Funktion zu erstellen:Die Verwendung ist auch einfach:
Hier ist ein Live-Beispiel: http://ideone.com/GKtkLQ
quelle
Sie können boost :: make_find_iterator nutzen. Ähnliches:
quelle
Hier ist mein Swiss® Army Knife von String-Tokenizern zum Aufteilen von Strings nach Leerzeichen, zum Berücksichtigen von Strings mit einfachen und doppelten Anführungszeichen sowie zum Entfernen dieser Zeichen aus den Ergebnissen. Ich habe RegexBuddy 4.x verwendet, um den größten Teil des Code-Snippets zu generieren , aber ich habe eine benutzerdefinierte Behandlung zum Entfernen von Anführungszeichen und ein paar anderen Dingen hinzugefügt.
quelle
Wenn die maximale Länge der zu tokenisierenden Eingabezeichenfolge bekannt ist, kann dies ausgenutzt und eine sehr schnelle Version implementiert werden. Ich skizziere die Grundidee unten, die sowohl von strtok () als auch von der Datenstruktur "Suffix Array" inspiriert wurde, die Jon Bentleys 2. Ausgabe von "Programming Perls", Kapitel 15, beschreibt. Die C ++ - Klasse bietet in diesem Fall nur eine gewisse Organisation und Bequemlichkeit von Nutzen. Die gezeigte Implementierung kann leicht erweitert werden, um führende und nachfolgende Leerzeichen in den Token zu entfernen.
Grundsätzlich kann man die Trennzeichen durch Zeichenfolgen-terminierende '\ 0'-Zeichen ersetzen und Zeiger auf die Token mit der geänderten Zeichenfolge setzen. Im Extremfall, wenn die Zeichenfolge nur aus Trennzeichen besteht, erhält man die Zeichenfolgenlänge plus 1 resultierende leere Token. Es ist praktisch, die zu ändernde Zeichenfolge zu duplizieren.
Header-Datei:
Implementierungsdatei:
Ein Nutzungsszenario wäre:
Ausgabe:
quelle
boost::tokenizer
ist Ihr Freund, aber erwägen Sie, Ihren Code in Bezug auf Internationalisierungsprobleme (i18n) portabel zu machen, indem Siewstring
/wchar_t
anstelle der Legacystring
/char
-Typen verwenden.quelle
wchar_t
ein schrecklicher implementierungsabhängiger Typ, den niemand verwenden sollte, es sei denn, dies ist absolut notwendig.Einfacher C ++ - Code (Standard C ++ 98) akzeptiert mehrere Trennzeichen (angegeben in einem std :: string) und verwendet nur Vektoren, Strings und Iteratoren.
quelle