Eingabepuffer leeren



  • Hallo miteinander,

    beim Versuch einen Reaktionstest in ANSI C++ für die Konsole zu schreiben, stieß ich auf ein Problem.

    #include <iostream>
    #include <stdlib.h>
    #include <ctime>
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
      cout << endl;
      char c;
      int n;
      double diff, best;
      clock_t c1, c2;
      char cstr[256];
    
      best = 0.5;
    
      while(true)
      {
        cout << "CLOCK STARTED..." << endl;
    
        n = (rand() % 10 + 5) * CLOCKS_PER_SEC;
        c1 = clock();
        while(n > clock() - c1)
        {}
    
        cout << "PRESS ENTER NOW!";
    
        // Wie leert man cin?
    
        c1 = clock();
        cin.get(c);
        c2 = clock();
        diff = (double(c2 - c1) / CLOCKS_PER_SEC);
        cout << "reaction time: " << diff << "s" << endl;
    
        ...
    
        cout << endl;
      }
      return 0;
    }
    

    Wenn man bei diesem Programm noch bevor PRESS ENTER NOW! erscheint die Eingabetaste betätigt, dann scheint die Reaktionszeit 0s zu sein. Wie kann man also den Eingabepuffer leeren?



  • cin.flush();
    

    Lg freeG



  • fr33g schrieb:

    cin.flush();
    

    Bitte lieber nachdenken bevor geschrieben wird -- .flush() gibt's nur für ostream -Objekte, nicht für istream -Objekte!
    http://www.cplusplus.com/reference/iostream/istream/


  • Mod

    fr33g schrieb:

    cin.flush();
    

    Lg freeG

    ‘struct std::istream’ has no member named ‘flush’
    

    @ccc: Kann dir leider auch nicht weiterhelfen, denn alle Methoden den Eingabepuffer zu leeren sind eher Behelfsmittel, da C++ die Eingabe nur als eine lange Reihe von Zeichen ansieht, ohne irgendwelche Informationen darüber in welchem Puffer sie gerade sind, wann sie eingegeben wurden oder woher sie kommen. Beispielsweise könnte man jedes C++ Programm ohne jede Änderung mit einer Datei anstatt einer Tastatur als Eingabe benutzen, dies ist sogar eine sehr verbreitete Methode. Und wie du dir sicherlich denken kannst, macht "Eingabepuffer leeren" bei einer Datei keinen Sinn, weil gar nicht klar ist was ein Eingabepuffer sein soll.
    Wenn du wirklich sicher gehen willst, dass die Eingabe von der Tastatur kommt, empfehle ich dir eine Konsolenbibliothek zu benutzen. Diese bieten dann natürlich auch Methoden um den Puffer zu leeren, denn wenn man weiß, dass die Eingabe von der Tastatur kommt, macht der Begriff "Eingabepuffer" auch einen Sinn. Leider sind die verschiedenen Konsolen zueinander ziemlich inkompatibel, weswegen du dich mit der Bibliothek auch auf ein System festlegen würdest. Eine verbreitete Bibliothek für POSIX-Konsolen ist beispielsweise ncurses.



  • ccc schrieb:

    fr33g schrieb:

    cin.flush();
    

    Bitte lieber nachdenken bevor geschrieben wird -- .flush() gibt's nur für ostream -Objekte, nicht für istream -Objekte!
    http://www.cplusplus.com/reference/iostream/istream/

    Ups, ja sorry wär in dem Fall besser gewesen 🙄

    Lg freeG



  • MetaInfo:
    1.) googel: c++ cin
    2.) click auf ersten Treffer : http://www.cplusplus.com/reference/iostream/cin/
    3.) lies:

    cin is an object of class istream that represents the standard input stream. I

    4.) click auf istream, scroll down, lies methoden

    ignore Extract and discard characters (public member functions)

    5.) click ignore, scroll down, lies und schau den Code an

    Mach eigenen code:
    Ignoriere ganz viele Zeichen vor deinen Textausgaben.
    Mach deine Textausgabe 1. Warte ... vor Textausgabe prüfe of Taste gedrückt wurde, wenn ja BEEEEEEEEEEEEEEEEEEEEEEEP, Verloren.
    Mach deine 2. Textausgabe, warte auf Taste und rechne Zeit


  • Mod

    padreigh schrieb:

    ...istream::ignore()...

    Keine gute Idee:

    www.cplusplus.com schrieb:

    `istream& ignore ( streamsize n = 1, int delim = EOF );

    `
    Extract and discard characters
    Extracts characters from the input sequence and discards them.

    The extraction ends when n characters have been extracted and discarded or when the character delim is found, whichever comes first. In the latter case, the delim character itself is also extracted.

    Hervorhebung durch mich. Der Satz heißt:

    Wenn man ignore macht und es sind nicht genug Zeichen zum ignorieren da, dann wird solange gewartet bis so viele Zeichen eingegeben wurden.

    Wenn man ignore macht und man gibt das newline-Zeichen '\n' (entspricht Enter-Taste) als Trennzeichen an, dann wird (sofern vorhanden) zwar das nächste newline-Zeichen ingoriert (sofern vorhanden, ansonsten wird gewartet), das nützt aber herzlich wenig wenn der Benutzer zweimal Enter drückt, denn dann wird nur das erste Drücken verworfen.

    Testprogramm:

    #include<iostream>
    using namespace std;
    
    int main()
    {
      int a,b;
      cout<<"A?"<<endl;
      cin>>a;
      cin.ignore(20);
      cout<<"B?"<<endl;
      cin>>b;
      cout<<"A: "<<a<<" B: "<<b<<endl;
    }
    

    Man achte darauf, dass nach der Abfrage von A das Programm stoppt und erstmal 20 Zeichen eingegeben werden müssen, bevor nach B gefragt wird.



  • SeppJ schrieb:

    Keine gute Idee:

    Jo stimmt, mein Fehler.

    Mit while(1) { peek(); if (good()) ignore(1); else break;} klappts auch nicht.
    Härtere Nuß als ich dachte 😉


  • Mod

    padreigh schrieb:

    SeppJ schrieb:

    Keine gute Idee:

    Jo stimmt, mein Fehler.

    Mit while(1) { peek(); if (good()) ignore(1); else break;} klappts auch nicht.
    Härtere Nuß als ich dachte 😉

    Wie schon gesagt, liegt dass daran, dass das einfach nicht vorgesehen ist. Das Modell der Eingabestreams ist nur eine lange Kette von Zeichen ohne jede Zusatzinformation. Die ganze technische Umsetzung mit eventuellen Pufferspeichern ist wegabstrahiert und mit Standardmitteln nicht zugänglich.



  • Ist zwar nicht C++ aber vielleicht hilfts ja weiter:

    fflush (stdin)
    Zumindest unter Windows löscht man damit den Eingabepuffer.
    http://msdn.microsoft.com/en-us/library/9yky46tz(VS.71).aspx



  • > Wie schon gesagt, liegt dass daran, dass das einfach nicht vorgesehen ist.

    Ich dachte, dafür gibt's std::basic_istream<char, std::char_traits<char>>::sync() .



  • Das kann man über den Stream Buffer machen.

    streambuf * pbuf;
    pbuf = cin.rdbuf();
    
    int size = pbuf->in_avail();
    cin.ignor(size);
    

    Lichtlein



  • Mit

    while(cin.get() =! '\n');
    

    wird cin geleert, jedoch muss man wieder die Eingabetaste drücken.



  • Ich verweise gern noch mal auf sync(). Alle anderen Lösungen sind Quatsch.



  • Lichtlein schrieb:

    streambuf * pbuf;
    pbuf = cin.rdbuf();
    
    int size = pbuf->in_avail();
    cin.ignor(size);
    

    Es verändert den Programmablauf leider irgendwie in keiner Weise.



  • Ad aCTa schrieb:

    Ich verweise gern noch mal auf sync(). Alle anderen Lösungen sind Quatsch.

    Mit sync() funktioniert es aber auch nicht!
    -->Test am Code


  • Mod

    Ad aCTa schrieb:

    > Wie schon gesagt, liegt dass daran, dass das einfach nicht vorgesehen ist.

    Ich dachte, dafür gibt's std::basic_istream<char, std::char_traits<char>>::sync() .

    Sicher? Also zumindest bei mir funktioniert das damit nicht. Und die Beschreibung im Standard was sync() machen soll ist ziemlich unverständlich, jedoch würde ich es nicht so interpretieren, dass das geforderte Verhalten folgt.

    Lichtlein schrieb:

    Das kann man über den Stream Buffer machen.

    streambuf * pbuf;
    pbuf = cin.rdbuf();
    
    int size = pbuf->in_avail();
    cin.ignor(size);
    

    Lichtlein

    Leider liefert in_avail() nur die Mindestzahl der lieferbaren Zeichen. Bei den meisten Streamtypen ist dies immer 0 😞 . Ansonsten würde es funktionieren.



  • #include <iostream>
    #include <string>
    
    void out_in_avail(std::ostream& os, std::streambuf* buf) {
    	os << buf->in_avail() << std::endl;
    }
    
    int main() {
    	std::string s1;
    	std::cin >> s1;
    	out_in_avail(std::cout, std::cin.rdbuf());
    	std::cin.sync();
    	out_in_avail(std::cout, std::cin.rdbuf());
    }
    

    Liefert bei mir:

    $ > Hallo
    $ > 1                   <--------- Das Newline-Zeichen
    $ > 0
    

    Und genau das soll es auch tun.



  • Wenn sync() nicht das tut, was es eigentlich sollte (nämlich alle Zeichen im Eingabepuffer zu verwerfen und den Stream damit zu synchronisieren), kann man es auch mal mit diesem dreckigen Hack probieren:

    #include <iostream>
    #include <string>
    
    void out_in_avail(std::ostream& os, std::streambuf* buf) {
    	os << buf->in_avail() << std::endl;
    }
    
    void iflush(std::istream& ios) {
    	std::ostream os(ios.rdbuf());
    	os.flush();
    	ios.rdbuf(os.rdbuf());
    }
    
    int main() {
    	std::string s1;
    	std::cin >> s1;
    	out_in_avail(std::cout, std::cin.rdbuf());
    	iflush(std::cin);
    	out_in_avail(std::cout, std::cin.rdbuf());
    	std::cin.get();
    }
    

  • Mod

    Nope, liefern bei mir beide

    Hallo
    0
    0
    

    und es wird am Ende beim get trotzdem nicht gewartet. Getestet unter einem Linux, sowohl mit dem neuesten GCC als auch mit dem neuesten Intel-Compiler.

    Und wie ich das sehe, ist dieses Verhalten auch durch den recht lax formulierten Standard gedeckt. Vermutlich gibt es auch gute Gründe dafür warum der Standard so formuliert ist und warum die Compilerhersteller sich nicht die Mühe geben es trotzdem besser zu machen, aber das würde zu ziemlich viel Spekulationen über Dinge führen mit denen ich mich nicht wirklich gut auskenne (Mein Wilder Tipp: Es hat wahrscheinlich was mit dem IO-Modell von POSIX zu tun).



  • Tja, VS 2008 und 2010 machen das so. Aber beim 2. Beispiel sollte es eigentlich überall klappen. Es sei denn, die Definition von ostream::flush() ist auch so lax.


Anmelden zum Antworten