Richtige Prüfung einer Eingabe cin? (Konsolenanwendung)



  • Hallo. Hab hier folgendes Beispiel.

    #include <iostream>
    #include <cmath>
    
    using namespace std;
    
    int main()
    {
        while ( true )
    	{
    		cout << "Von welcher Zahl soll die Wurzel gezogen werden? ";
    
            double eingabe;
            cin >> eingabe;
    
            if (cin.good() && eingabe >= 0)
            {
                double wurzel = sqrt(eingabe);
                cout << "Die Wurzel von " << eingabe << " = " << wurzel << endl;
            }
            else if (cin.good() && eingabe < 0)
            {
               cout << "Negativen Zahlen sind verboten!" << endl;
            }
            else if (cin.fail())
            {
                cin.clear();
                cin.sync();
    
                cout << "Buchstaben und Sonderzeichen sind verboten!" << endl;
            }
            else
            {
                cin.clear();
                cin.sync();
    
                cout << "fataler Fehler oder EOF" << endl;
            }
        }
    
    	return 0; 
    }
    

    Was kann ich machen damit nach der Eingabe zB 123a der Fall "else if (cin.fail())" eintritt ohne dabei einen goodbit Status zu bekommen?

    Das Problem ist doch folgendes. Man bekommt in diesem Fall durch cin zwei Ausgaben. 123 und a.
    Leerzeichen sind bei der Eingabe auch ganz fies -.-.

    Ich möchte 123a in einer Ausgabe haben.

    Würde hier eine Konvertierung in einen string helfen? Oder gibt es noch andere Möglichkeiten, besser Alternativen? Oder soll man hier get, getch, getchar oder sowas verwenden?

    Ich versuche da schon seit Tagen drauf zu kommen -.- Kann mir bitte jemand weiter helfen?



  • Du kannst eof prüfen, ob alle Zeichen gelesen worden sind.


  • Mod

    Wenn deine Definition einer Fehleingabe ist, dass zwischen zwei Whitespaces alles komplett zu parsen sein muss, dann kann dir ein Zwischenweg über einen String tatsächlich helfen.
    Aber:
    * Ich als Poweruser würde mich von deinem Programm unnötig bevormundet fühlen.
    * Dem Normaluser wird es wahrscheinlich sowieso egal sein.

    Ich verstehe auch nicht folgende Bemerkung:

    Do7 schrieb:

    Leerzeichen sind bei der Eingabe auch ganz fies -.-.

    Vermutlich denkst du an noch ein anderes Eingabemodell. Beschreib am besten mal genau, was du dir vorstellt.

    DocShoe schrieb:

    Du kannst eof prüfen, ob alle Zeichen gelesen worden sind.

    😕
    eof funktioniert so nicht. eof misst, ob das Ende eines Streams überschritten wurde und hat überhaupt nichts mit dem Inhalt zu tun.



  • Ich möchte die Berechnung der Wurzel verhindern.

    1. Wenn der Benutzer zB Zahlen mit Buchstaben mischt. zB 123a
    2. Wenn der Benutzer Leerzeichen in der Eingabe verwendet. (Ich möchte eine Zahl haben und nicht mehrere)

    Alles was nicht "einem" doubleWert entspricht soll als Falsch angesehen werden.



  • SeppJ schrieb:

    * Ich als Poweruser würde mich von deinem Programm unnötig bevormundet fühlen.
    * Dem Normaluser wird es wahrscheinlich sowieso egal sein.

    Das sehe ich komplett anders. Vielleicht bewegen wir uns in unterschiedlichen Feldern. Ich möchte immer und sofort sehen, wenn etwas nicht so wie erwartet ist. Da darf mich das Programm auch gerne bevormunden.

    Wenn eine Zahl erwartet wird und 123a kommt, dann soll das gefälligst auffallen (exception, Programmende, Weltuntergang oder ähnlich), aber auf gar keinen Fall Erfolg liefern, 123 als Zahl sehen und "a" für die nächste Eingabe vorsehen!

    Stell dir nur mal die tolle Eingabe "12O21 String" vor. Wenn ich z.B. erst eine Zahl, dann einen String lesen will, kommt mit normalem Gebrauch von >> als Zahl 12 heraus und als String "O21 String". Es hätte aber 12021 als Zahl und "String" als String herauskommen sollen. Solche Fehler in Eingabedaten sieht man dann nicht. Daher finde ich es gerade als Poweruser unbrauchbar, wenn sowas ungesehen durch geht. Also: als String lesen und dann mit boost::lexical_cast konvertieren, da stod und ähnliche ebenfalls Buchstaben durchgehen lassen, wenn die Zeichen davor wie ein String aussehen. Das ist zumindest mein aktueller Weg, der mir allerdings nicht unbedingt effizient vorkommt.

    Oder macht man es so?

    double d; char ch; string s;
    while (is >> d >> ch && ch == ' ' && is >> s >> ch && ch == '\n') ...
    

    oder ähnlich? Und wenn dann der Fehler aufgetreten ist, wie kann ich dann sinnvoll ausgeben, was ich gerade gelesen habe (am besten die gesamte Zeile)?

    Ich habe zwar hier schon diverse Posts von Werner Salomon gelesen, aber ich finde das korrekte Einlesen mit Fehlerkontrolle und Streamoperatoren extrem schwierig.


  • Mod

    Aber jetzt kommt er sofort noch damit, dass er keine Leerzeichen als Trennzeichen zulassen möchte. Der einzige Grund dafür ist tastaturzentriertes Denken, kein fachlicher Grund. So ein Programm könnte man nur interaktiv in einer Kommandozeile benutzen, obwohl's bloß ein simpler Rechner ist. Warum diese künstlichen Einschränkungen?



  • Leerzeichen: ich hatte neulich auch die tolle Zahl "42 000" oder "2'300" (oder ähnlich). Jedenfalls sollte das eine 42000 sein, nicht eine 42 gefolgt von einer 0 bzw. eine 2300, keine 2 + irgendwas. Daher: Space als Trenner ist viel viel zu oft in Daten drin, wo eigentlich kein Space sein sollte.

    Vielleicht bin ich aber auch einfach nur genervt von den Daten, die ich so bekomme, von denen behauptet wird, die seien absolut "sauber"... Das beste waren noch Datumsfelder im Format aa.bb.yyyy, wobei yyyy das Jahr war. Wenn aa <= 12 und bb <= 12, war aa = Tag und bb = Monat, sonst aa = Monat und bb = Tag (oder umgekehrt, weiß nicht mehr)... ARGH!



  • Spricht irgendetwas (außer dass es umständlich ist) gegen das hier:

    #include <iostream>
    #include <sstream>
    #include <string>
    
    int main() 
    {
    	double d;
    	std::string s;
    	std::cout << "Eingabe: ";
    	std::getline(std::cin, s);
    	if (std::cin.fail())
    	{
    		std::cout << "ERROR";
    	}
    	else
    	{
    		std::istringstream iss(s);
    		iss >> d;
    		if (iss.eof())
    		{
    			std::cout << d;
    		}
    		else
    		{
    			std::cout << "ERROR";
    		}
    		iss.clear();
    	}
    
    	std::cin.get();
    	return 0;
    }
    

    Scheint nämlich soweit alles als Fehler zu melden, was nicht passt.



  • Finde es blöd, den String erst von cin in s, dann in iss und letztendlich in d zu kopieren.

    Du könntest auch in deinem Originalstream lesen und mit peek auf \n testen.

    bool isEolOrEof(int c) { 
        return c == char_traits<char>::eof() 
            || c == char_traits<char>::to_int_type('\n');
    }
    
    ...
    
    if (cin >> d && isEolOrEof(cin.peek())) {
        //alles gut, arbeite mit d
    } else {
        cout << "Nä, alles großer Mist!\n";
    }
    

    So, ich bin aber bei weitem kein Stream-Experte, ich hasse sie sogar. Geht bestimmt schöner.



  • Ich hatte es mal so gemacht, weiß nicht ob es "gut" oder "fachlich korrekt" ist, aber mich hatte es auch mal gestört, dass "123a" einfach als "123" durch geht, wenn man eine Zahl einließt.

    template<typename T>
    T get_num(const std::string& prompt)
    {   // Gets a number of type T from std::cin, rejecting input like "123a"
        std::cout << prompt;
        std::string input;
    
        while (!trim(input).size())  // skip empty input lines
            if (!std::getline(std::cin, input))  // EOF or cin.bad()
                throw std::runtime_error("No Input");
    
        std::istringstream stream{ input };
        T n;
        if (stream >> n)
        {   // test for remaining characters
            char ch;
            if (!(stream >> ch)) return n;
        }
        std::cout << "Format Error.\n";
        return get_num<T>(prompt);
    }
    

Anmelden zum Antworten