3379 Stimmen

Wie kann ich über die Wörter einer Zeichenkette iterieren?

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

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

Beachten Sie, dass ich nicht in C-String-Funktionen oder diese 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 im Moment 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?

693 Stimmen

Kumpel... Eleganz ist in meinen Augen nur eine schicke Umschreibung für "Effizienz, die hübsch aussieht". Scheuen Sie sich nicht, C-Funktionen und schnelle Methoden zu verwenden, um etwas zu erreichen, nur weil es nicht in einer Vorlage enthalten ist ;)

19 Stimmen

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

0 Stimmen

@nlaq, Außer, dass Sie Ihr String-Objekt mit c_str() konvertieren müssten, und wieder zurück in einen String, wenn Sie es immer noch benötigen, um eine Zeichenfolge zu sein, nicht?

178voto

Alec Thomas Punkte 17213

Hier ist eine andere Lösung. Sie ist kompakt und einigermaßen 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 lässt sich leicht als Vorlage verwenden, um Stringtrennzeichen, breite Strings usw. zu verarbeiten.

Beachten Sie, dass die Aufteilung "" ergibt eine einzige leere Zeichenkette und die Aufteilung "," (d. h. sep) ergibt zwei leere Zeichenfolgen.

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

std::vector<std::string> split(const std::string &text, char sep) {
    std::vector<std::string> tokens;
    std::size_t start = 0, end = 0;
    while ((end = text.find(sep, start)) != std::string::npos) {
        if (end != start) {
          tokens.push_back(text.substr(start, end - start));
        }
        start = end + 1;
    }
    if (end != start) {
       tokens.push_back(text.substr(start));
    }
    return tokens;
}

Wenn eine Zeichenkette an mehreren Begrenzungszeichen aufgeteilt werden soll, während leere Token übersprungen werden, 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 Stimmen

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

3 Stimmen

Die Ausgabe wird aus Effizienzgründen als Parameter übergeben. Wenn das Ergebnis zurückgegeben würde, wäre entweder eine Kopie des Vektors oder eine Heap-Allokation erforderlich, die dann wieder freigegeben werden müsste.

0 Stimmen

Mein Fehler, ich war fälschlicherweise davon ausgegangen, dass STL würde Lazy Copy verwenden, wie Qt Container tun. Schade, dass sie es nicht tun.

142voto

gnomed Punkte 5249

Dies ist meine Lieblingsmethode, um durch eine Zeichenkette zu iterieren. 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...
}

1 Stimmen

Ist es möglich, zu erklären word als char ?

1 Stimmen

Tut mir leid, Abatischtschew, C++ ist nicht meine Stärke. Aber ich kann mir vorstellen, dass es nicht schwierig wäre, eine innere Schleife hinzuzufügen, um jedes Zeichen in jedem Wort zu durchlaufen. Ich glaube aber, dass die derzeitige Schleife auf Leerzeichen zur Worttrennung angewiesen ist. Es sei denn, Sie wissen, dass es nur ein einzelnes Zeichen zwischen jedem Leerzeichen, in diesem Fall können Sie nur Cast "Wort" zu einem Char... sorry ich kann nicht von mehr Hilfe, ive gewesen Bedeutung auf meine C++ auffrischen

12 Stimmen

Wenn Sie word als char deklarieren, wird jedes Zeichen, das kein Leerzeichen ist, durchlaufen. Es ist einfach genug, um es zu versuchen: stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;

89voto

Ferruccio Punkte 96076

Diese Frage ähnelt der Frage von Stack Overflow Wie kann ich eine Zeichenkette in C++ tokenisieren? . Erfordert externe Boost-Bibliothek

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

0 Stimmen

Wird dabei eine Kopie aller Token materialisiert, oder wird nur die Start- und Endposition des aktuellen Tokens gespeichert?

71voto

Shadow2531 Punkte 11620

Ich mag das folgende, weil es die Ergebnisse in einen Vektor setzt, unterstützt eine Zeichenfolge als Delim und gibt Kontrolle über leere Werte zu halten. Aber es sieht dann 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() das teilweise so funktioniert. Und wenn Sie mit "Weißraum" wirklich jede Art von Weißraum meinen, können Sie Boosts Split mit is_any_of() funktioniert hervorragend.

0 Stimmen

Endlich eine Lösung, die leere Token auf beiden Seiten der Zeichenkette korrekt behandelt

60voto

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

Sie können jedoch entweder C's strtok() Funktion durch die Verwendung der std::string::c_str() Mitglied, oder Sie können Ihr eigenes schreiben. Hier ist ein Codebeispiel, das ich nach einer schnellen Google-Suche gefunden habe ( "STL-Zeichenkettenaufteilung" ):

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++Programmierung-HOWTO-7.html

Wenn Sie Fragen zum Code-Beispiel haben, hinterlassen Sie einen Kommentar und ich werde es Ihnen erklären.

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

Lassen Sie sich nicht für dumm verkaufen "Eleganz vor Leistung" handeln.

0 Stimmen

Ich bin mir der C-String-Funktionen bewusst, und ich bin mir auch der Leistungsprobleme bewusst (beides habe ich in meiner Frage erwähnt). Für diese spezielle Frage suche ich jedoch nach einer eleganten C++-Lösung.

0 Stimmen

... und Sie wollen nicht einfach einen OO-Wrapper über die C-Funktionen bauen, warum?

11 Stimmen

@Nelson LaQuet: Lass mich raten: Weil strtok nicht reentrant ist?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X