2017-05-07 17 views
0

Ich soll Wörter aus einer Textdatei lesen. Wort ist als eine fortlaufende Buchstabenfolge definiert. Also zum Beispiel in der folgenden Zeichenfolge:Alphabetische Zeichen nur aus Datei lesen - C++

"Es ist eine veryy gute #" Idee einer Linie. Sie wissen es? "

die Worte sind:

es sa ver y gute Idee der Linie wissen Sie

('it' und 'a' verdoppelt)

Ich habe mich gefragt, , wenn es irgendeine schlaue Funktion gibt, die Wörter liest, bis sie einen nicht-alphabetischen Charakter findet? Oder der einzige Weg, dies zu tun, ist char nach Char zu lesen und push_back zu verwenden, bis wir nicht-alphabetische finden?

+0

Sehen Sie, wie Sie Trennzeichen bearbeiten können ... – Charles

+1

Die beste Technik besteht darin, aus der Datei in eine Zeichenfolge zu lesen und dann die unerwünschten Zeichen aus der Zeichenfolge zu entfernen. –

+0

[Std:; Getline] (http://en.cppreference.com/w/cpp/string/basic_string/getline) akzeptiert einen Delimiter-Parameter ... EDIT: Aber das gibt Ihnen nur ein Delimiter nach dem anderen ... Hoppla. – Charles

Antwort

0

Wenn Sie eine Zeichenfolge aus einem Stream lesen, liest der Stream eine zusammenhängende Folge von Nicht-Leerzeichen als Zeichenfolge. Es ignoriert dann alle Leerzeichen. Das nächste Zeichen ohne Leerzeichen ist der Anfang der nächsten Zeichenfolge, die gelesen wird. Das ist so ziemlich das gewünschte Verhalten, mit einer weiteren Ausnahme: Sie möchten, dass alles andere als Buchstaben wie Leerraum behandelt wird.

Glücklicherweise codiert der Stream seine Idee von "weißem Raum" nicht fest. Es verwendet ein Gebietsschema, um ihm mitzuteilen, was Leerraum ist. Ein Locale setzt sich wiederum aus Teilen zusammen, die sich mit einzelnen Aspekten ("Facetten") der Lokalisierung befassen. Die Facette, die sich speziell mit der Klassifizierung von Charakteren befasst, ist eine ctype Facette. Wenn wir also eine ctype-Facette schreiben, die alles andere als einen Buchstaben als Leerraum klassifiziert, können wir ganz einfach "Wörter" aus dem Stream lesen.

Hier einige Code genau das zu tun:

struct alpha_only: std::ctype<char> { 

    alpha_only(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(std::ctype<char>::table_size,std::ctype_base::space); 

     std::fill(&rc['a'], &rc['z'], std::ctype_base::lower); 
     std::fill(&rc['A'], &rc['Z'], std::ctype_base::upper); 
     return &rc[0]; 
    } 
}; 

Die char Spezialisierung einer ctype Facette (immer) Tisch angetrieben. Alles, was wir wirklich tun müssen, ist eine Tabelle zu erstellen, die Zeichen richtig klassifiziert. In diesem Fall bedeutet dies, dass alphabetische Zeichen als Groß- oder Kleinbuchstaben klassifiziert werden und alles andere als Leerraum klassifiziert wird. Wir tun das, indem wir die Tabelle mit ctype_base::space füllen, dann für die alphabetischen Zeichen, die im Grunde sagen: "oops, nein, das ist kein Leerzeichen, das ist Groß- oder Kleinbuchstaben.

Technisch ist die Art, wie ich das gemacht habe leicht falsch - es geht davon aus, dass Groß- und Kleinbuchstaben zusammenhängend sind. Dies gilt für jeden vernünftigen Zeichensatz, aber nicht von EBCDIC. Wenn wir technisch korrekt sein wollten, statt der beiden "std :: fill" Anrufe, wir eine Schleife so etwas schreiben könnte:

auto max = std::numeric_limits<unsigned char>::max(); 

for (int i=0; i<max; i++) 
    if (islower(i)) 
     table[i] = std::ctype_base::lower; 
    else if (isupper(i)) 
     table[i] = std::ctype_base::upper; 
    else 
     table[i] = std::ctype_base::space; 

So oder so, ist die Schlussfolgerung ziemlich einfach: Großschreibung ist Großbuchstaben, Kleinbuchstaben Kleinschreibung ist, alles andere ist „white space“

0123.

Sobald wir das geschrieben haben, müssen wir dem Stream mitteilen, dass dieses Gebietsschema verwendet werden soll; dann können wir unsere Worte wirklich leicht lesen:

int main() { 
    std::istringstream infile("It’s a ver5y good #” idea of a line. You know it?"); 

    // Tell the stream to use our character classifier: 
    infile.imbue(std::locale(std::locale(), new alpha_only)); 

    std::string word; 
    while (infile >> word) 
     std::cout << word << "\n"; 
} 

[I eine neue Linie zwischen jedem „Wort“ gesetzt haben, so können Sie leicht sehen, was es als ein Wort liest.

]

Ergebnis:

It 
s 
a 
ver 
y 
good 
idea 
of 
a 
line 
You 
know 
it 

Basierend auf Ihrem Ergebnis in der Frage, Sie offenbar auch nur jedes Wort sollen in der Ausgabe einmal erscheinen. Um dies zu tun, würden Sie normalerweise jedes Wort in ein Set als dessen gelesenes Element einfügen und es nur in die Ausgabe schreiben, wenn das Einfügen in das Set erfolgreich war.

std::unordered_set<std::string> words; 
std::string word; 

while (infile >> word) 
    if (words.insert(word).second) 
     std::cout << word << "\n"; 

Die insert für setunordered_set und liefert einen pair<iterator, bool>, wo das anzeigt, ob bool Insertion erfolgreich war. Wenn es vorher vorhanden war, wird das fehlschlagen und false zurückgeben, basierend darauf entscheiden wir, ob wir das Wort schreiben sollen oder nicht.

Mit dieser Änderung erscheint it immer noch in der Ausgabe zweimal - die erste Instanz hat die i aktiviert, und die zweite nicht. Um das herauszufiltern, müssen Sie jeden String vollständig in Kleinbuchstaben (oder vollständig in Großbuchstaben) konvertieren, bevor Sie ihn in den Satz einfügen.

+0

Danke! Es ist ein bisschen schwieriger, als ich erwartet habe, aber scheint alle meine Bedürfnisse zu decken :) – Yksisarvinen

Verwandte Themen