Endlosschleife warum?



  • Hallo,
    ich versuche mich gerade daran eine Funktion zu schreiben, die einen Fehler bei der Eingabe abfängt. Also wenn ich z.B Buchstaben in eine float Variable eingebe. Anstatt einer neuen Möglichkeit zum Eingeben, läuft das ganz als Endlosschleife durch, wenn ich die Funktion rekursiv wieder aufrufe...
    Liegt es evtl. daran, dass der Speicher von cin gelöscht werden muss? Wenn ja wie mache ich das?
    Hier der Code für die Funktion:

    float eingabeVp(void)
    {
        cout <<"Bitte geben Sie Vp ein: " <<endl;
        if(!(cin >> Vp))
        {
            cin.clear();
            Vp = eingabeVp();
        }
        return Vp;
    
    }
    

    Danke schon mal für die Hilfe!



  • Den rekursiven Aufruf würd ich nicht machen, sondern eine einfache Schleife verwenden. Ansonsten füg mal folgendes hinter std::clear() ein:

    std::cin.ignore( std::cin.rdbuf()->in_avail() );
    


  • Ich habe das ganze auch als Schleife versucht, aber auch dort das gleiche Problem.

    Auch wenn ich

    std::cin.ignore( std::cin.rdbuf()->in_avail() );
    

    einfüge ändert sich nichts...Bin auch noch nicht wirklich fit in C++ und schon seit Stunden dabei rumzuprobieren aber es funktioniert einfach nicht 😞



  • Wenn cin sich den Kram nicht direkt im eigenen streambuf-Objekt vorhält (bzw. die Konsole das Buffering übernimmt) kommst du damit nicht wirklich weiter, will sagen, der Code erzeugt äußerst undefiniertes Verhalten. Sinnvoller ist es, die Eingabe gleich zeilenweise einzulesen und zu interpretieren - dann gibts auch keine Probleme mit Einlesefehlern oder Rückständen im Stream.

    Das sieht dann so aus:

    #include <iostream>
    #include <sstream>
    #include <string>
    
    float eingabeVp() {
      std::string zeile;
      std::istringstream isstr;
      float ergebnis;
    
      do {
        std::cout << "Bitte geben Sie Vp ein: " << std::flush;
    
        std::getline(std::cin, zeile);
    
        isstr.clear();
        isstr.str(zeile);
        isstr >> ergebnis;
      } while(!isstr);
    
      return ergebnis;
    }
    

    ...oder in sinnvollerer Weise auch

    template <typename t> t read_type(std::string const &prompt) {
      std::string zeile;
      std::istringstream isstr;
      t ergebnis;
    
      do {
        std::cout << prompt << std::flush;
    
        std::getline(std::cin, zeile);
    
        isstr.clear();
        isstr.str(zeile);
        isstr >> ergebnis;
      } while(!isstr);
    
      return ergebnis;
    }
    
    // ...
    
    float vp = read_type<float>("Bitte geben Sie Vp ein: ");
    


  • hmm.

    do{
        cin.clear();
        cin.ignore(cin.rdbuf()->in_avail());
        cin>>variable;
    }while(cin.fail());
    

    Sollte gehen, denke ich. Versuchs mal.



  • cin.rdbuf()->in_avail()
    

    ist nicht die Anzahl der Zeichen, die noch in stdin drinsteht, sondern die Anzahl der Zeichen im von std::cin gehaltenen Streambuf-Objekt. Das ist hier völlig irrelevant, und wenn das überhaupt funktioniert, dann nur zufällig mit der STL-Implementierung deines Compilers. Darüber hinaus kriegst du damit ganz arge Probleme, wenn dir jemand ne Datei nach stdin füttert oder vergleichbares - dieser Code ist bestenfalls ein ganz dreckiger Hack, der im Kontext von Standard-C++ völlig undefiniertes Verhalten erzeugt. Und sowas wollen wir doch gern vermeiden, oder?

    Für die Aufgabe "Schmeiß alles raus, was noch im Konsolenpuffer steht" bietet Standard-C++ keine Möglichkeit. Das liegt daran, dass sich das Programm nicht drauf verlassen kann, dass es mit einem interaktiven Terminal verbunden ist. Wenn du so eine Funktionalität willst, musst du dich bei betriebssystemsspezifischen Bibliotheken/APIs umsehen. (termio unter POSIX-Systemen, conio unter Windows) Allerdings ist das äußerst selten die beste Möglichkeit damit umzugehen.



  • Achso, das war mir neu. Es hat bisher noch jedesmal funktionniert bei mir (Visual C++ Express 2005) Hab ich wohl Glück gehabt.



  • cin.ignore(numeric_limits<streamsize>::max(), '\n');
    

    ...sollte aber unabhängig vom bereits gesagten immer alle Zeichen bis zum nächsten Zeilenwechsel aus dem Stream verwerfen. Da die Eingabepufferung der Konsole (meistens) zeilenbasiert arbeitet sollte man damit recht gut fahren, ohne auf API-Funktionen zurückgreifen zu müssen, selbst wenn man eine Datei an stdin verfüttert.



  • @ seldon: Danke der Code funktioniert wunderbar!

    @Shinja: Der Code läuft bei mir immer noch in der Endlosschleife...
    Danke trotzdem..

    Gruß S



  • seldon schrieb:

    ...
    Für die Aufgabe "Schmeiß alles raus, was noch im Konsolenpuffer steht" bietet Standard-C++ keine Möglichkeit....

    Den Verdacht hatte ich auch schon (nachdem ich mir auch lange mit in_avail() vergeblich die Finger gebrochen habe) .... gut, dass DEu das jetzt bestätigst.

    Gruß,

    Simon2.



  • LordJaxom schrieb:

    ...sollte ... immer ... meistens...

    Mit dieser Wortkombination habe ich meistens immer Schwierigkeiten. 😉

    Gruß,

    Simon2.



  • Inwiefern steht die Aussage, dass dieser Aufruf immer alle Zeichen bis zum nächsten '\n' liest im Widerspruch zu der Aussage dass die Konsole meistens zeilenbasiert arbeitet?

    EDIT:
    Ergänzend dazu, wenn sie zeichenweise arbeitet ists auch egal, dann muss die Implementierung (Streambuffer) nur kleinere Häppchen verarbeiten, wenn sie rein blockweise arbeitet kann man praktisch eh nicht mit Konsolenprogrammen arbeiten.

    Und "sollte immer" interpretiere ich als "ich meine, ..." (Ausdruck für nicht 100%ige Sicherheit) "...das ist immer so" (Ausdruck dafür, dass keine Abweichung vorkommt) 😉



  • LordJaxom schrieb:

    Inwiefern steht die Aussage, dass dieser Aufruf immer alle Zeichen bis zum nächsten '\n' liest im Widerspruch zu der Aussage dass die Konsole meistens zeilenbasiert arbeitet?...

    Nun, mit einem fetten "immer" könnte jemand halt auf die Idee kommen, dass Du hier eine Lösung vorschlägst, die eben "immer" funktioniert.... was aber für nicht-zeilenbasierte Konsolen doch nicht zutrifft. 😃

    Gruß,

    Simon2.



  • Simon2 schrieb:

    was aber für nicht-zeilenbasierte Konsolen doch nicht zutrifft. 😃

    Dazu siehe EDIT 😉

    Wie gesagt auf blockbasierten Konsolen kann man sich die Konsolenprogrammierung eh schenken, da nicht garantiert ist dass beim Druck der Enter-Taste überhaupt schon was beim Programm ankommt.



  • Ergo: Für zeilenbasierte Konsolen ist Dein Vorschlag prima, für alle anderen weiß man's nicht. => Es gibt nicht "DEN 100%-igen Weg" ... man hängt immer am Wohl und Wehe der jewiligen Konsole. Aber immerhin sind "die meisten" zeilenbasiert.

    Mehr wollte ich gar nicht aussagen - typisches Beispiel eben für die Kombination "meistens immer" 😉

    Gruß,

    Simon2.


Log in to reply