So testen Sie, ob der Stringstream-Operator>> einen fehlerhaften Typ analysiert und übersprungen hat

Lesezeit: 7 Minuten

So testen Sie ob der Stringstream Operator einen fehlerhaften Typ analysiert
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?

  • Ü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

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).

  • Ja, das habe ich sofort getan, nachdem Sie diese Antwort auf diese andere Frage verlinkt hatten. Hervorragende und schnelle Arbeit! +1

    – Fantastischer Herr Fuchs

    1. Juli 2014 um 23:42 Uhr

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)

1647261011 260 So testen Sie ob der Stringstream Operator einen fehlerhaften Typ analysiert
πάντα ῥεῖ

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

  • Das ist gut, aber vielleicht möchten Sie die while(1)-Schleife überarbeiten. Wenn am Ende der Eingabe ein ungültiges Doppelzeichen steht, wird es für immer wiederholt. z.B ideone.com/3HSyxH

    – Fantastischer Herr Fuchs

    24. September 2014 um 23:33 Uhr


  • @ Ben while(iss) vielleicht?

    – πάντα ῥεῖ

    24. September 2014 um 23:35 Uhr

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

1647261011 341 So testen Sie ob der Stringstream Operator einen fehlerhaften Typ analysiert
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.

  • Wenn cin >> input scheitert an eof, der if Aussage wird ausgewertet true – leicht behoben ala if (cin >> input && (...)) ....

    – Toni Delroy

    31. März 2015 um 2:24 Uhr

  • @TonyD Du hast völlig Recht, es ist wichtig, sich an diese Technik zu erinnern input enthalten etwas.

    – Galik

    31. März 2015 um 11:32 Uhr

  • Wenn cin >> input scheitert an eof, der if Aussage wird ausgewertet true – leicht behoben ala if (cin >> input && (...)) ....

    – Toni Delroy

    31. März 2015 um 2:24 Uhr

  • @TonyD Du hast völlig Recht, es ist wichtig, sich an diese Technik zu erinnern input enthalten etwas.

    – Galik

    31. März 2015 um 11:32 Uhr

1001550cookie-checkSo testen Sie, ob der Stringstream-Operator>> einen fehlerhaften Typ analysiert und übersprungen hat

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy