cin seltsames Verhalten in while-Schleife mit Plausibilitätsprüfung



  • Gemäss dem untenstehenden Beispiel möchte ich eine double-Variable mit cin eingeben, dabei erfolgt eine Plausibilitätsprüfung der Eingabe. Je nach Eingabe kommt es zu einem (mir) rätselhaften Verhalten von cin. Ich bin nur Hobbyprogrammierer und den untenstehenden Code kann man sicherlich besser programmieren. Ich verstehe es aber trotzdem nicht... 😞 Ich möchte ja auch eigentlich eine Eingabe von Buchstaben verhindern, denn MASSE ist ein double...
    Wie mache ich das???

    double MASSE;
    
    while ( true )
        {
          cout << "Bitte Eingabe Masse" << endl;
          fflush (stdin);
          cin >> MASSE;
          fflush (stdin);
          if ( MASSE >= 10 ) break;
          else
          {
            cout << "Dies ist keine gueltige Eingabe!" << endl;
    	fflush (stdin);
          }
        }
    

    Bei Eingabe von z.B. 3 ist alles i.O. - es wird erneut abgefragt.
    Bei Eingabe von z.B. 13 ist alles i.O. - es geht weiter.
    Wenn ich zuerst 3 eingebe und dann bei der erneuten Aufforderung zur Eingabe diesmal z.B. df eingebe, bin ich plötzlich in einer Endlosschleife gefangen.
    Wenn ich z.B. df oder f sofort eingebe, geht es weiter obwohl das natürlich nicht korrekt ist (double-Variable).
    Wie kann ich diese Eingabe narrensicher machen?? Helft mir bitte mal weiter!



  • Scanf hätte den Vorteil, dass Dir die Funktion mitteilt, wieviele Werte erfolgreich eingelesen wurden. Wenn scanf einen Wert kleiner als die Anzahl der erwarteten Eingabewerte zurückgibt, dann weißt Du, dass der User groben Unfug eingegeben hat. Mit einer while(fgetc(stdin)!='\n'); Schleife kannst Du dann den Eingabebuffer ausräumen. Und dann gibst Du dem User erneut eine Chance, vernünftige und ausreichend viele Werte einzugeben.
    Am besten man sperrt den User in einem do ... while Konstrukt ein! Solange, bis scanf Dir mitteilt, dass genügend Werte erfolgreich eingelesen wurden.
    Leider schweigt der Transferoperator der Streams sehr ausgiebig, wenn man abfragen möchte, wieviele Werte erfolgreich eingelesen wurden. Also besser scanf verwenden. Ausnahmsweise.



  • Edit: das war noch nicht so doll, lieber wieder entfernen. 😃



  • Hi,

    cin >> MASSE;
    if(cin.fail()) // fail() = input/output Fehler, zB bei char in int eingelesen
    {
        cin.clear(); // Fehlerflags löschen
        std::string temp = "";
        getline(cin,temp); // Müll ausm Buffer weglesen
        // raus break, return sonstwas
    }
    


  • #include <iostream>
    #include <string>
    #include <sstream>
    
    bool readDouble(double& value)
    {
    	std::string line;
    	std::getline(std::cin, line);
    	std::istringstream iss(line);
    
    	if(iss >> value)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    int main()
    {
    	double masse;
    
    	while(!readDouble(masse))
    	{
    		std::cout << "Falsche Eingabe!" << "\n";
    	}
    
    	std::cout << "Masse: " << masse << "\n";
    }
    


  • proggingmania schrieb:

    #include <iostream>
    using namespace std;
     
    int main()
    {	
        double dMasse = 10;
    	cin.clear();
    	while ( dMasse < 0 || dMasse >= 10 ) 
        { 
          cout << "Bitte Eingabe Masse" << endl; 
             cin >> dMasse; 
       } 
     return 0;
    }
    

    wird nicht hinhauen bei ner eingabe von zeichen , da bist du wieder in endlosschleife.. versuch die eingabe lieber per string und wandel das um,
    strings lassen sich besser kontrollieren
    du kannst ja mal anschauen was nach der fehlerhaften eingabe bei den Fehlerstromflags passiert

    cout << "After a bad input operation:"
    		<< "\ncin.rdstate(): " << cin.rdstate()
    		<< "\n cin.eof(): " << cin.eof()
    		<< "\n cin.fail(): " << cin.fail()
    		<< "\n cin.bad(): " << cin.bad()
    		<< "\n cin.good(): " << cin.good() << endl << endl;
    	cin.clear(); // clear stream
    	// display results of cin functions after clearing cin
    	cout << "After cin.clear()" << "\ncin.fail(): " << cin.fail()
    		<< "\ncin.good(): " << cin.good() << endl;
    

    wenn das failbit gesetzt ist kommt es zur endlosschleife. egal ob da noch ein cin kommt oder net.



  • meine is auch noch nicht ganz richtig. 😞



  • bin überwältigt... danke mal an alle!
    muss das erstmal sortieren, es gibt anscheinend unendliche Möglichkeiten.
    Muss dazu sagen, dass ich eigentlich C programmiere (mehr schlecht als recht 😉 ], C++ kann man das nicht nennnen. daher tue ich mir auch schwer mit gewissen Vorschlägen von Euch. Wenn ich den Code ansehe ist mir vieles plausibel und manches verstehe ich nicht. habe z.B. schon krampfhaft nach diesen Möglichkeiten für cin.irgendwas gesucht, wo ist denn das dokumentiert. Kenne nur cin.getline. das benutze ich auch, weil ich ein Beispiel habe...

    Also wenn ihr noch mehr code habt, nur zu, werde dann morgen mal die für mich plausibelsten Lösungen testen, danke! 🙂 🙂



  • sten schrieb:

    wird nicht hinhauen bei ner eingabe von zeichen , da bist du wieder in endlosschleife.. versuch die eingabe lieber per string und wandel das um,
    strings lassen sich besser kontrollieren
    du kannst ja mal anschauen was nach der fehlerhaften eingabe bei den Fehlerstromflags passiert

    Stimmt, daher hab ich es lieber gleich wieder rausgenommen, damit das niemand unnötigerweise kopiert und ausprobiert.

    Hab mir jetzt eine Lösung zurechtgebastelt, die deiner Beschreibung entspricht und bombensicher ist. 🙂

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	double dMasse;
    	char buf[80];
    	char *err_ptr = NULL;
    
    	do
    	{
    		err_ptr = NULL;
    		cout << "Bitte geben sie die Masse ein: " << endl; 
            cin.getline( buf, 80 );
    		dMasse = strtod( buf, &err_ptr );
    	}
    	while ( *err_ptr != '\0' );
    
       return 0;
    }
    

    Edit: '}' Klammer vergessen 😃
    Edit: Bombensicher ist das doch noch nicht, nämlich wenn man mehr als 80 Zeichen eingibt. Dann wird die Eingabe ja abgeschnitten und man erhält eine kleinere Zahl als eingegeben. Shit.

    🙂



  • ich hoffe jetzt ist meins richtig:

    bool readDouble(double& value)
    {
    	std::string line;
    	std::getline(std::cin, line);
    	std::istringstream iss(line);
    
    	if(iss >> value)
    	{
    		iss >> std::ws; // Leerzeichen verschlucken
    		return iss.eof();
    	}
    
    	return false;
    }
    


  • @proggingmania: wenn man enter drückt wird die schleife schon verlassen. 😉



  • Würde folgendes funktionnieren?

    int main(){
        double d=0.0;
        do{
            while(!(cin>>d)){
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(),'\n'); //danke an David_pb (siehe unten)
            }
        }while(d>0.0 && d<10.0);
    


  • Meine gängige/gebräuchlische Lösung interessiert wohl niemanden ...



  • bombensicher?? schrieb:

    @proggingmania: wenn man enter drückt wird die schleife schon verlassen. 😉

    Peinlich peinlich 😃



  • KasF schrieb:

    Meine gängige/gebräuchlische Lösung interessiert wohl niemanden ...

    Klar, immer her damit. 😉
    Edit: Das ist keine Lösung, nur ein Fragment 😉



  • Shinja schrieb:

    Würde folgendes funktionnieren?

    int main(){
        double d=0;
        do(){
            while(!(cin>>d)){
                cin.clear();
                cin.ignore(32000,'\n); //wie war das mit den numeric_limits<stream_buf>() nochmal? Ka wie das richtig war...
            }
        }while(d<10);
    
    std::cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
    


  • #include <iostream>
    #include <string>
    using namespace std;
    
    int main()
    {
    
    	double dMasse;
    	string eingabe;
    	char *err_ptr = NULL;
    
    	do
    	{
    		err_ptr = NULL;
    		cout << "Bitte geben sie die Masse ein( Werte 0 bis 10 ):  " << endl; 
            cin >> eingabe;
    		dMasse = strtod( eingabe.c_str(), &err_ptr );
    	}
    	while (  *err_ptr != '\0' || ( dMasse < 0 || dMasse > 10 ) );
    
    	cout << "Sie haben bombensicher einen Wert zwischen 0 und 10 "
    		"eingegeben: " << dMasse << endl;
    
       return 0;
    }
    

    🙂



  • Aus euren sehr guten Vorschlägen habe ich jetzt zwei Versionen ausgewählt, die einwandfrei laufen, besonderen Dank an proggingmania und KasF:
    Hab sie noch etwas vervollständigt.

    proggingmania:

    #include <iostream> 
    #include <string> 
    using namespace std; 
    
    // funktioniert  
    
    int main() 
    
    { 
    
        double MASSE; 
        string eingabe; 
        char *err_ptr = NULL; 
    
        do 
        { 
            err_ptr = NULL; 
            cout << "Bitte geben sie die Masse ein (Wert >= 10):  " << endl; 
            cin >> eingabe; 
            MASSE = strtod( eingabe.c_str(), &err_ptr ); 
        } 
        while (  *err_ptr != '\0' || ( MASSE < 10) ); 
    
        cout << "Sie haben bombensicher einen Wert >= 10 " 
            "eingegeben: " << MASSE << endl; 
    
       return 0; 
    }
    

    KasF:

    // funktioniert
    
    #include <iostream> 
    #include <string> 
    using namespace std; 
    
    int main ()
    {
    
      double MASSE;
    
    while ( true )
    {
    cout << "Bitte geben sie die Masse ein:  " << endl; 
    cin >> MASSE; 
    if(cin.fail()) // fail() = input/output Fehler, zB bei char in int eingelesen 
    { 
        cout << "Eingabe einer Zahl >= 10 erforderlich!" << endl; 
        cin.clear();
        fflush (stdin);
        continue;
    }   
    
    if ( MASSE < 10 ) cout << "Masse zu klein!" << endl;
    if ( MASSE >= 10 ) cout << "Eingabe akzeptiert!" << endl;
    if ( MASSE >= 10 ) break;
    
    }
    cout << "Masse = " << MASSE << endl;
    return 0;
    
    }
    


  • fflush (stdin);
    

    🙄



  • Shinja schrieb:

    Würde folgendes funktionnieren?

    int main(){
        double d=0.0;
        do{
            while(!(cin>>d)){
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(),'\n'); //danke an David_pb (siehe unten)
            }
        }while(d>0.0 && d<10.0);
    

    Jetzt muss ich mich doch mal selbst zitieren: warum ist diese (für mich pers. einfacherer Variante) nicht praktikabel? (Frage zum eigenen Verständnis)

    EDIT: nochn Syntaxfehler drin gehabt...


Anmelden zum Antworten