Ich versuche, über die Wörter einer Zeichenfolge zu iterieren.
Es kann davon ausgegangen werden, dass die Zeichenfolge aus Wörtern besteht, die durch Leerzeichen getrennt sind.
Beachten Sie, dass ich nicht an C-String-Funktionen oder dieser Art von Zeichenmanipulation/Zugriff interessiert bin. Bitte geben Sie in Ihrer Antwort auch der Eleganz den Vorrang vor der Effizienz.
Die beste Lösung, die ich jetzt habe, ist:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
string s = "Somewhere down the road";
istringstream iss(s);
do
{
string subs;
iss >> subs;
cout << "Substring: " << subs << endl;
} while (iss);
}
Gibt es eine elegantere Möglichkeit, dies zu tun?
Für das, was es wert ist, hier ist eine weitere Möglichkeit, Tokens aus einer Eingabezeichenfolge zu extrahieren, wobei nur auf Standardbibliotheksfunktionen zurückgegriffen wird. Es ist ein Beispiel für die Kraft und Eleganz hinter dem Design des 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 Ausgabestrom zu kopieren, könnte man sie unter Verwendung desselben Generikums in einen Container einfügen copy
Algorithmus.
vector<string> tokens;
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter(tokens));
… oder erstellen Sie die vector
direkt:
vector<string> tokens{istream_iterator<string>{iss},
istream_iterator<string>{}};
#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;
}
Für diejenigen, denen es nicht passt, alle Effizienz für die Codegröße zu opfern und “effizient” als eine Art Eleganz zu sehen, sollte das Folgende 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;
}
}
Ich entscheide mich normalerweise für die Verwendung std::vector<std::string>
Typen als mein zweiter Parameter (ContainerT
)… aber list<>
ist viel schneller als vector<>
für den Fall, dass kein direkter Zugriff erforderlich ist, und Sie können sogar Ihre eigene Zeichenfolgenklasse erstellen und so etwas wie verwenden std::list<subString>
wo subString
macht keine Kopien für unglaubliche Geschwindigkeitssteigerungen.
Es ist mehr als doppelt so schnell wie das schnellste Tokenize auf dieser Seite und fast fünfmal schneller als einige andere. Auch mit den perfekten Parametertypen können Sie alle Zeichenfolgen- und Listenkopien für zusätzliche Geschwindigkeitssteigerungen eliminieren.
Außerdem führt es nicht die (äußerst ineffiziente) Rückgabe des Ergebnisses durch, sondern übergibt die Tokens als Referenz, sodass Sie auch Tokens mit mehreren Aufrufen erstellen können, wenn Sie dies wünschen.
Schließlich können Sie über einen letzten optionalen Parameter angeben, ob leere Token aus den Ergebnissen entfernt werden sollen.
Alles was es braucht ist std::string
… der Rest ist optional. Es verwendet keine Streams oder die Boost-Bibliothek, ist aber flexibel genug, um einige dieser fremden Typen natürlich akzeptieren zu können.
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 mit Vorlagen versehen werden, um Zeichenfolgentrenner, breite Zeichenfolgen usw. zu handhaben.
Beachten Sie diese Aufteilung ""
führt zu einer einzelnen leeren Zeichenfolge und Aufspaltung ","
(zB sep) ergibt zwei leere Strings.
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 Aufteilen einer Zeichenfolge an mehreren Trennzeichen unter Ü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;
}
10036400cookie-checkWie iteriere ich über die Wörter einer Zeichenfolge?yes
Alter … Eleganz ist in meinem Buch nur eine schicke Art, “Effizienz-die-hübsch-aussieht” zu sagen. Scheuen Sie sich nicht, C-Funktionen und schnelle Methoden zu verwenden, um etwas zu erreichen, nur weil es nicht in einer Vorlage enthalten ist;)
– Benutzer19302
25. Oktober 2008 um 9:04 Uhr
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
– pyon
29. September 2009 um 15:47 Uhr
@Eduardo: Das ist auch falsch … Sie müssen iss zwischen dem Versuch, einen anderen Wert zu streamen, und der Verwendung dieses Werts testen, dh
string sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
– Toni Delroy
11. April 2012 um 2:24 Uhr
Verschiedene Optionen in C++, um dies standardmäßig zu tun: cplusplus.com/faq/sequences/strings/split
– hB0
31. Oktober 2013 um 0:23 Uhr
Eleganz ist mehr als nur hübsche Effizienz. Zu den eleganten Attributen gehören eine geringe Zeilenzahl und eine gute Lesbarkeit. IMHO Eleganz ist kein Proxy für Effizienz, sondern Wartbarkeit.
– Matt
31. März 2017 um 13:22 Uhr