478 Stimmen

Wie kann ich eine Zeichenkette in C++ tokenisieren?

Java verfügt über eine praktische Split-Methode:

String str = "The quick brown fox";
String[] results = str.split(" ");

Gibt es eine einfache Möglichkeit, dies in C++ zu tun?

234 Stimmen

Ich kann nicht glauben, dass diese Routineaufgabe in C++ so viel Kopfzerbrechen macht

6 Stimmen

Seine nicht Kopfschmerzen in C + + - es gibt verschiedene Möglichkeiten, um es zu erreichen. Programmierer sind weniger bewusst, C + + als c # - seine über Marketing und Investitionen ... siehe dies für verschiedene C + + Optionen, um das gleiche zu erreichen: cplusplus.com/faq/sequenzen/strings/split

11 Stimmen

@hB0 gehen durch viele Fragen Antworten und immer noch nicht entscheiden, bedeutet, ist ein Kopfschmerz. die eine braucht, dass die Bibliothek, die andere ist nur für Leerzeichen, die andere nicht behandeln Leerzeichen.

9voto

Jonathan Mee Punkte 36373

Adam Pierce's Antwort bietet einen handgesponnenen Tokenizer, der eine const char* . Bei Iteratoren ist es etwas problematischer, weil Inkrementierung einer string der End-Iterator ist undefiniert . Angesichts der Tatsache, dass string str{ "The quick brown fox" } können wir dies sicherlich erreichen:

auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };

while (start != cend(str)) {
    const auto finish = find(++start, cend(str), ' ');

    tokens.push_back(string(start, finish));
    start = finish;
}

Live Example


Wenn Sie die Komplexität abstrahieren wollen, indem Sie Standardfunktionen verwenden, wie On Freund schlägt vor strtok ist eine einfache Option:

vector<string> tokens;

for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i);

Wenn Sie keinen Zugang zu C++17 haben, müssen Sie Folgendes ersetzen data(str) wie in diesem Beispiel: http://ideone.com/8kAGoa

Dies wird in dem Beispiel jedoch nicht gezeigt, strtok müssen nicht für jedes Token das gleiche Trennzeichen verwenden. Neben diesem Vorteil gibt es jedoch auch einige Nachteile:

  1. strtok kann nicht für mehrere Personen verwendet werden strings zur gleichen Zeit: Entweder ein nullptr muss übergeben werden, um die Tokenisierung des aktuellen string oder eine neue char* tokenize übergeben werden (es gibt einige nicht standardisierte Implementierungen, die dies jedoch unterstützen, wie z.B.: strtok_s )
  2. Aus demselben Grund strtok kann nicht auf mehreren Threads gleichzeitig verwendet werden (dies kann jedoch z. B. durch die Implementierung festgelegt werden: Die Implementierung von Visual Studio ist thread-sicher )
  3. Aufruf von strtok string der es betrieben wird, so dass es nicht verwendet werden kann bei const string s, const char* s oder wörtliche Zeichenketten, um diese mit strtok oder zum Betrieb einer string deren Inhalt bewahrt werden muss, str kopiert werden müsste, dann könnte die Kopie bearbeitet werden

c++20 versorgt uns mit split_view Zeichenketten auf nicht-destruktive Weise tokenisieren: https://topanswers.xyz/cplusplus?q=749#a874


Die bisherigen Methoden können keine tokenisierte vector in-place, d.h. ohne sie in eine Hilfsfunktion zu abstrahieren, können sie nicht initialisiert werden const vector<string> tokens . Diese Funktionalität und die Fähigkeit zu akzeptieren cualquier Leerraumbegrenzer kann mit einem istream_iterator . Zum Beispiel gegeben: const string str{ "The quick \tbrown \nfox" } können wir dies tun:

istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };

Live Example

Die erforderliche Konstruktion eines istringstream Diese Option ist mit weitaus höheren Kosten verbunden als die beiden vorangegangenen Optionen, die sich jedoch in der Regel in den Kosten für string Zuweisung.


Wenn keine der oben genannten Optionen flexibel genug für Ihre Tokenisierungsanforderungen ist, ist die flexibelste Option die Verwendung einer regex_token_iterator Natürlich bringt diese Flexibilität auch höhere Kosten mit sich, aber auch diese sind wahrscheinlich in den Kosten versteckt. string Verteilungskosten. Nehmen wir zum Beispiel an, wir wollen auf der Basis von nicht-abgeschnittenen Kommas tokenisieren, die ebenfalls Leerraum fressen, und geben die folgende Eingabe ein: const string str{ "The ,qu\\,ick ,\tbrown, fox" } können wir dies tun:

const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };

Live Example

7voto

sohesado Punkte 79

Sehen Sie sich dieses Beispiel an. Es könnte Ihnen helfen

#include <iostream>
#include <sstream>

using namespace std;

int main ()
{
    string tmps;
    istringstream is ("the dellimiter is the space");
    while (is.good ()) {
        is >> tmps;
        cout << tmps << "\n";
    }
    return 0;
}

6voto

Jim In Texas Punkte 1514

MFC/ATL hat einen sehr guten Tokenizer. Von MSDN:

CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;

resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
   printf("Resulting token: %s\n", resToken);
   resToken= str.Tokenize("% #",curPos);
};

Output

Resulting Token: First
Resulting Token: Second
Resulting Token: Third

4voto

Fawix Punkte 469

Sie können einfach eine Bibliothek für reguläre Ausdrücke und lösen das Problem mit regulären Ausdrücken.

Ausdruck verwenden ( \w +) und die Variable in \1 (oder $1, je nach der Bibliotheksimplementierung der regulären Ausdrücke).

4voto

jilles de wit Punkte 6990

Für einfache Dinge verwende ich einfach das Folgende:

unsigned TokenizeString(const std::string& i_source,
                        const std::string& i_seperators,
                        bool i_discard_empty_tokens,
                        std::vector<std::string>& o_tokens)
{
    unsigned prev_pos = 0;
    unsigned pos = 0;
    unsigned number_of_tokens = 0;
    o_tokens.clear();
    pos = i_source.find_first_of(i_seperators, pos);
    while (pos != std::string::npos)
    {
        std::string token = i_source.substr(prev_pos, pos - prev_pos);
        if (!i_discard_empty_tokens || token != "")
        {
            o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
            number_of_tokens++;
        }

        pos++;
        prev_pos = pos;
        pos = i_source.find_first_of(i_seperators, pos);
    }

    if (prev_pos < i_source.length())
    {
        o_tokens.push_back(i_source.substr(prev_pos));
        number_of_tokens++;
    }

    return number_of_tokens;
}

Feiger Haftungsausschluss: Ich schreibe Echtzeit-Datenverarbeitungssoftware, bei der die Daten über Binärdateien, Sockets oder einen API-Aufruf (E/A-Karten, Kameras) eingehen. Ich verwende diese Funktion nie für etwas Komplizierteres oder Zeitkritischeres als das Einlesen externer Konfigurationsdateien beim Starten.

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