
Fantastischer Herr Fuchs
Ich bin daran interessiert, Methoden für die Verwendung zu diskutieren stringstream
um eine Zeile mit mehreren Typen zu analysieren. Ich würde mit der folgenden Zeile beginnen:
"2.832 1.3067 nana 1.678"
Nehmen wir nun an, ich habe eine lange Zeile mit mehreren strings
und doubles
. Der offensichtliche Weg, dies zu lösen, besteht darin, die Zeichenfolge zu tokenisieren und dann die Konvertierung jeder einzelnen zu überprüfen. Ich bin daran interessiert, diesen zweiten Schritt zu überspringen und zu verwenden stringstream
direkt, um nur die Nummern zu finden.
Ich dachte, ein guter Weg, dies zu erreichen, wäre, die Zeichenfolge durchzulesen und zu prüfen, ob die failbit
wurde gesetzt, was der Fall sein wird, wenn ich versuche, eine Zeichenfolge in ein Double zu zerlegen.
Angenommen, ich habe den folgenden Code:
string a("2.832 1.3067 nana 1.678");
stringstream parser;
parser.str(a);
for (int i = 0; i < 4; ++i)
{
double b;
parser >> b;
if (parser.fail())
{
std::cout << "Failed!" << std::endl;
parser.clear();
}
std::cout << b << std::endl;
}
Es wird Folgendes ausgedruckt:
2.832
1.3067
Failed!
0
Failed!
0
Ich bin nicht überrascht, dass eine Zeichenfolge nicht analysiert werden kann, aber was intern passiert, so dass es seine nicht löschen kann failbit
und die nächste Zahl parsen?
Der folgende Code funktioniert gut, um die zu überspringen schlechtes Wort und sammeln Sie die gültigen double
Werte
istringstream iss("2.832 1.3067 nana 1.678");
double num = 0;
while(iss >> num || !iss.eof()) {
if(iss.fail()) {
iss.clear();
string dummy;
iss >> dummy;
continue;
}
cout << num << endl;
}
Hier ist ein voll funktionsfähiges Muster.
Ihr Beispiel hat es fast richtig gemacht, es hat nur gefehlt, das ungültige Eingabefeld aus dem Stream zu konsumieren, nachdem das falsche Format erkannt wurde
if (parser.fail()) {
std::cout << "Failed!" << std::endl;
parser.clear();
string dummy;
parser >> dummy;
}
In Ihrem Fall wird die Extraktion versuchen, erneut zu lesen "nana"
für die letzte Iteration, daher die letzten beiden Zeilen in der Ausgabe.
Beachten Sie auch die Trickserei iostream::fail()
und wie man tatsächlich testet iostream::eof()
in meiner 1. probe. Es gibt eine bekannte Frage und Antwort, warum das einfache Testen auf EOF als Schleifenbedingung als falsch angesehen wird. Und es beantwortet gut, wie die Eingabeschleife unterbrochen werden kann, wenn unerwartete/ungültige Werte gefunden wurden. Aber wie man ungültige Eingabefelder überspringt/ignoriert, wird dort nicht erklärt (und wurde auch nicht danach gefragt).
Einige geringfügige Unterschiede zur Antwort von πάντα ῥεῖ – macht es auch möglich, z. B. negative Zahlendarstellungen usw. zu verarbeiten, und ist – IMHO – etwas einfacher zu lesen.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
double num = 0;
for (; iss; )
if (iss >> num)
std::cout << num << '\n';
else if (!iss.eof())
{
iss.clear();
iss.ignore(1);
}
}
Ausgabe:
2.832
1.3067
1.678
-100
0.05
(sehe es laufen Hier)

πάντα ῥεῖ
Ich habe dafür eine feiner abgestimmte Version gebaut, die ungültige Eingaben zeichenweise überspringen kann (ohne dass getrennt werden muss double
Zahlen mit Leerzeichen):
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
istringstream iss("2.832 1.3067 nana1.678 xxx.05 meh.ugh");
double num = 0;
while(iss >> num || !iss.eof()) {
if(iss.fail()) {
iss.clear();
while(iss) {
char dummy = iss.peek();
if(std::isdigit(dummy) || dummy == '.') {
// Stop consuming invalid double characters
break;
}
else {
iss >> dummy; // Consume invalid double characters
}
}
continue;
}
cout << num << endl;
}
return 0;
}
Ausgabe
2.832
1.3067
1.678
0.05
Live-Demo
Wenn Sie Prägnanz mögen – hier ist eine weitere Option, die (ab?) verwendet &&
bekommen cout
wird nur ausgeführt, wenn eine Zahl erfolgreich geparst wurde, und wenn eine Zahl nicht geparst wird, verwendet sie den Komma-Operator, um dies zu können clear()
Stream-Fehlerstatus innerhalb der Bedingung vor dem Lesen eines zu ignorierenden Zeichens …
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
double num = 0;
char ignored;
while (iss >> num && std::cout << num << '\n' ||
(iss.clear(), iss) >> ignored)
;
}
http://ideone.com/WvtvfU

Galik
Sie können verwenden std::istringstream::eof()
zu bestätigen so eingeben:
#include <string>
#include <sstream>
#include <iostream>
// remove white-space from each end of a std::string
inline std::string& trim(std::string& s, const char* t = " \t")
{
s.erase(s.find_last_not_of
s.erase(0, s.find_first_not_of
return s;
}
// serial input
std::istringstream in1(R"~(
2.34 3 3.f 3.d .75 0 wibble
)~");
// line input
std::istringstream in2(R"~(
2.34
3
3.f
3.d
.75
0
wibble
)~");
int main()
{
std::string input;
// NOTE: This technique will not work if input is empty
// or contains only white-space characters. Therefore
// it is safe to use after a conditional extraction
// operation >> but it is not reliable after std::getline()
// without further checks.
while(in1 >> input)
{
// input will not be empty and will not contain white-space.
double d;
if((std::istringstream(input) >> d >> std::ws).eof())
{
// d is a valid double
std::cout << "d1: " << d << '\n';
}
}
std::cout << '\n';
while(std::getline(in2, input))
{
// eliminate blank lines and lines
// containing only white-space (trim())
if(trim(input).empty())
continue;
// NOW this is safe to use
double d;
if((std::istringstream(input) >> d >> std::ws).eof())
{
// d is a valid double
std::cout << "d2: " << d << '\n';
}
}
}
Das funktioniert, weil die eof()
check stellt das sicher nur Das Doppelte wurde eingetragen und nicht wie Müll 12d4
.
10015500cookie-checkSo testen Sie, ob der Stringstream-Operator>> einen fehlerhaften Typ analysiert und übersprungen hatyes
Überprüfen Sie bitte meine Antwort hier: C++ bewegt sich zum nächsten Element in einer Datei.txt. Ich denke, es ist relevant.
– πάντα ῥεῖ
1. Juli 2014 um 7:24 Uhr
@πάνταῥεῖ Ahhh, ok, also bleibt es beim ersten Fehler hängen.
– Fantastischer Herr Fuchs
1. Juli 2014 um 7:27 Uhr
@πάνταῥεῖ Ich habe die Löschung auf Ihren Wunsch hin rückgängig gemacht. Nach Ihrem vorgeschlagenen Beitrag war ich mir ziemlich sicher, dass es sich um ein Duplikat handelt. Aber wenn Sie eine Antwort hinzufügen möchten, wäre das großartig.
– Fantastischer Herr Fuchs
1. Juli 2014 um 22:42 Uhr
@πάνταῥεῖ Ich denke, das ist ziemlich einfach, richtig. Ich mache eine for-Schleife und lese nur 4 Mal, weil ich weiß, wie lang sie sein sollte. Wenn es die Zeichenfolge nicht parsen kann und daher nicht weitergeht, wird es einfach nicht erneut parsen. Die Verwendung des Codes in Ihrer vorgeschlagenen Antwort liefert das Ergebnis:
2.832 1.3067 Failed! 0 1.678
– Fantastischer Herr Fuchs
1. Juli 2014 um 23:19 Uhr
Ich fürchte, ich habe so viele Fragen mit gesendet
!istream::eof()
unnötigerweise als Duplikate geschlossen werden. Warten, bis jemand die findet richtig komplett Betrüger für diesen. Vielen Dank für deine Mühe @Ben! Ich denke, dies ist ein guter Ausgangspunkt für eine kanonische Antwort auf eine FAQ.– πάντα ῥεῖ
2. Juli 2014 um 0:05 Uhr