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 set
unordered_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.
Sehen Sie, wie Sie Trennzeichen bearbeiten können ... – Charles
Die beste Technik besteht darin, aus der Datei in eine Zeichenfolge zu lesen und dann die unerwünschten Zeichen aus der Zeichenfolge zu entfernen. –
[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