Wie verwerfe ich bereits eingegebene Daten in std::istream?



  • Hallo,

    Ich suche eine Art flush für std::cin.
    Vorweg sollte ich sagen, dass die
    hier http://forums.devx.com/archive/index.php/t-87000.html
    und hier http://www.daniweb.com/forums/thread90228.html
    vorgestellten Lösungen mir leider nicht geholfen haben. Diese führen eher dazu, dass jeder zweite String ignoriert wird, was nicht das ist, was ich möchte.

    Ein Testprogramm zur Konkretisierung:

    #include <iostream>
    
    int main()
    {
        while (true)
        {
            std::cout << "> ";
            std::string line;
            std::getline(std::cin, line);
            std::cout << "echo " << line << std::endl;
        }
        return 0;
    }
    

    Wenn ich nun so etwas wie

    a
    b
    c
    

    (also durch newline getrennte Strings) in die Konsole per copy & paste einfüge, wird die oben gezeigte Schleife drei Mal durchlaufen. Ich möchte allerdings nur den aller ersten String haben und den Rest ignorieren. Das heißt, ich benötige eine Möglichkeit bereits eingegebenen Input zu verwerfen ohne noch nicht eingegebenen Input zu beeinträchtigen.

    Hat jemand eine Lösung?
    Dank im Voraus!



  • Schildere mal genauer Dein Vorhaben.
    Ist doch logisch, dass wenn Du drei Zeilen eingibst (und nichts anderes macht dieses c/p ja), Dir std::cin auch drei Zeilen zurueckgibt, wenn Du es in einer Schleife ausfuehrst.
    Du willst nur die erste Zeile? Entferne "while(true)".

    Andernfalls gibts auch noch std::cin.ignore, was MOEGLICHERWEISE in die Richtung geht, da ich aber nicht wirklich weiss was Du vorhast, kann man ich nur raten.



  • Grüß dich!

    Vielleicht hilft dir diese std-Liste weiter...
    Einfach die richtigen Befehle zusammenwürfeln!

    😉



  • cpploser schrieb:

    Schildere mal genauer Dein Vorhaben.
    Ist doch logisch, dass wenn Du drei Zeilen eingibst (und nichts anderes macht dieses c/p ja), Dir std::cin auch drei Zeilen zurueckgibt, wenn Du es in einer Schleife ausfuehrst.
    Du willst nur die erste Zeile? Entferne "while(true)".

    Andernfalls gibts auch noch std::cin.ignore, was MOEGLICHERWEISE in die Richtung geht, da ich aber nicht wirklich weiss was Du vorhast, kann man ich nur raten.

    Es geht ja gerade darum, mehrere Zeilen nacheinander einzulesen. Allerdings soll es eben nur möglich sein, jeweils nur eine Zeile einzugeben.
    Um das zu verdeutlichen das while(true).
    Ich möchte sozusagen eine Zeile einlesen, und dann den restlichen Pufferinhalt ignorieren, bis eine weitere Eingabe getätigt wird.
    Bei c/p werden jedoch direkt mehrere Zeilen eingegeben. Dies soll verhindert werden.
    Mit std::istream.ignore() habe ich bereits gespielt.
    Das Problem liegt darin zu entscheiden, wann man die Grenze von bereits eingegebenen zu noch nicht eingegebenen Zeilen überschreitet.


  • Mod

    keiner schrieb:

    Das Problem liegt darin zu entscheiden, wann man die Grenze von bereits eingegebenen zu noch nicht eingegebenen Zeilen überschreitet.

    Das ist deine Definitionssache, es gibt keinen technischen Unterschied. Wie soll deine Definition lauten?



  • Wenn ich dich richtig verstanden habe dann Versuchs mal damit:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main() 
    {    
         string x;
         while(1)
         {
           cout << "Daten eingeben : ";
           getline(cin,x);
           cout << "Nur diese Daten verarbeiten: " << x << endl;
           while (x.length() > 0)
           {
               cout << "Weitere Zeilen einlesen: ";
               getline(cin,x);
               cout << "Zeile verworfen: " << x << endl;
           } 
         }
     return 0;
    }
    

    EDIT: #include <string> vergessen, while schleife erweitert



  • SeppJ schrieb:

    keiner schrieb:

    Das Problem liegt darin zu entscheiden, wann man die Grenze von bereits eingegebenen zu noch nicht eingegebenen Zeilen überschreitet.

    Das ist deine Definitionssache, es gibt keinen technischen Unterschied. Wie soll deine Definition lauten?

    Okay. Sagen wir \n steht innerhalb der Eingabe für newline (Der String "\n" ist bei Eingabe in der Konsole ja eben nichts weiter als ein String.). Falls, zb per c/p, mehrere Zeilen gleichzeitig eingegeben werden, erwarte ich in etwa dieses Verhalten:

    > a\nb\nc\n
    echo a // Zeilen "b" und "c" werden verworfen
    > foo\nbar
    echo foo // Zeile "bar" wird verworfen
    

    Ich würde also folgende Definition geben:
    Bereits eingegebene Daten: Daten, die sich im Puffer des Streams befinden.
    Noch nicht eingegebene Daten: Daten, die sich (da noch nicht eingegeben 🙄) noch nicht im Puffer des Streams befinden.

    @DeepCopy: Dein Beispiel erfordert ein zweites newline, um die innere Schleife zu verlassen. Bis auf diese Tatsache, relativ genau das, was ich suche.

    ...übrigens scheint istream::seekg() auch nicht weiter zu helfen 😞


  • Mod

    keiner schrieb:

    Ich würde also folgende Definition geben:
    Bereits eingegebene Daten: Daten, die sich im Puffer des Streams befinden.
    Noch nicht eingegebene Daten: Daten, die sich (da noch nicht eingegeben 🙄) noch nicht im Puffer des Streams befinden.

    Folgender Befehl, tut am ehesten das, was deine Definition besagt:

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

    Problem: Er macht nicht das, was du willst, denn in_avail gibt nur die Anzahl der Zeichen zurück, die noch mindestens im Lesepuffer sind und das ist in den meisten Fällen 0, selbst bei copy&paste. Ich fürchte, dein Problem ist so nicht lösbar.

    Das tiefergehende Problem dabei ist folgendes:
    Wenn die Sequenz "a\nb\nc\n" eingetippt wird, dann trägt diese keinerlei Information darüber, wann die verschiedenen newlines getippt wurden. Für den stream sieht beides gleich aus: <"a\nb\nc\n"> = <"a\n" Pause b\n" Pause "c\n"> denn der stream sieht wirklich nur die einzelnen Zeichen. Es wäre wahrscheinlich möglich, das von dir gewünschte umzusetzen, wenn man irgendetwas zeitgesteuertes verwendet, dann muss man aber wieder definieren, was das richtige Zeitintervall ist.



  • SeppJ schrieb:

    Das tiefergehende Problem dabei ist folgendes:
    Wenn die Sequenz "a\nb\nc\n" eingetippt wird, dann trägt diese keinerlei Information darüber, wann die verschiedenen newlines getippt wurden. Für den stream sieht beides gleich aus: <"a\nb\nc\n"> = <"a\n" Pause b\n" Pause "c\n"> denn der stream sieht wirklich nur die einzelnen Zeichen. Es wäre wahrscheinlich möglich, das von dir gewünschte umzusetzen, wenn man irgendetwas zeitgesteuertes verwendet, dann muss man aber wieder definieren, was das richtige Zeitintervall ist.

    Naja, in irgendeiner Queue müssen die eingegebenen Zeichen ja gespeichert werden, und ich war (optimistischerweise) in der Annahme, das dies in std::streambuf geschehen könnte. Von dieser Seite betrachtet ist es also weniger eine Frage nach dem Zeitpunkt der Eingabe der Zeichen.

    Mit Streams scheint das Problem also nicht lösbar zu sein. Gibt es vielleicht eine andere Möglichkeit mit C-Funktionen?
    Ich befürchte, dass oben genannte Zeichen-Queue im Tastaturtreiber realisiert wird. Wenn ich mein Problem also mit Treiberspielereien lösen würde, würde ich damit also systemweit alle Eingaben verarbeiten und nicht nur die, die für das Konsolenfenster bestimmt sind... hmm...


  • Mod

    keiner schrieb:

    SeppJ schrieb:

    Das tiefergehende Problem dabei ist folgendes:
    Wenn die Sequenz "a\nb\nc\n" eingetippt wird, dann trägt diese keinerlei Information darüber, wann die verschiedenen newlines getippt wurden. Für den stream sieht beides gleich aus: <"a\nb\nc\n"> = <"a\n" Pause b\n" Pause "c\n"> denn der stream sieht wirklich nur die einzelnen Zeichen. Es wäre wahrscheinlich möglich, das von dir gewünschte umzusetzen, wenn man irgendetwas zeitgesteuertes verwendet, dann muss man aber wieder definieren, was das richtige Zeitintervall ist.

    Naja, in irgendeiner Queue müssen die eingegebenen Zeichen ja gespeichert werden, und ich war (optimistischerweise) in der Annahme, das dies in std::streambuf geschehen könnte. Von dieser Seite betrachtet ist es also weniger eine Frage nach dem Zeitpunkt der Eingabe der Zeichen.

    Mit Streams scheint das Problem also nicht lösbar zu sein. Gibt es vielleicht eine andere Möglichkeit mit C-Funktionen?
    Ich befürchte, dass oben genannte Zeichen-Queue im Tastaturtreiber realisiert wird. Wenn ich mein Problem also mit Treiberspielereien lösen würde, würde ich damit also systemweit alle Eingaben verarbeiten und nicht nur die, die für das Konsolenfenster bestimmt sind... hmm...

    C Funktionen helfen auch nicht weiter. Ich habe mal versucht mittels

    std::ios::sync_with_stdio(false);
    

    die Art der Synchronisation zu ändern. Die Synchronisation erfolgt aber anscheinend immer bei den newlines, insofern nützt dies nichts, wenn man immer nur ganze Zeilen ausliest.

    edit: Wenn man nur für ein bestimmtes Betriebssystem programmiert, kann man vielleicht noch was machen. Musste mal im passenden forum fragen, falls dies in Frage kommt.



  • SeppJ schrieb:

    C Funktionen helfen auch nicht weiter.

    Bist du sicher?

    SeppJ schrieb:

    Ich habe mal versucht mittels

    std::ios::sync_with_stdio(false);
    

    die Art der Synchronisation zu ändern. Die Synchronisation erfolgt aber anscheinend immer bei den newlines, insofern nützt dies nichts, wenn man immer nur ganze Zeilen ausliest.

    Wie hast du eigentlich herausgefunden, dass die Synchronisation bei newlines geschieht? Und wie genau soll mir die Art dieser bei meinem Problem helfen?

    Ich versuche Mal mit fgets() und setvbuf() zu spielen, mit dem man die Art der Pufferung (zeilenweise, voll, gar nicht) einstellen kann.
    Ansonsten werde ich wohl einfach mit dem Problem leben müssen.

    Trotzdem vielen Dank für deine Zeit!


  • Mod

    keiner schrieb:

    SeppJ schrieb:

    C Funktionen helfen auch nicht weiter.

    Bist du sicher?

    Nein. Aber ziemlich sicher, siehe unten.

    SeppJ schrieb:

    Ich habe mal versucht mittels

    std::ios::sync_with_stdio(false);
    

    die Art der Synchronisation zu ändern. Die Synchronisation erfolgt aber anscheinend immer bei den newlines, insofern nützt dies nichts, wenn man immer nur ganze Zeilen ausliest.

    Wie hast du eigentlich herausgefunden, dass die Synchronisation bei newlines geschieht?

    Nur mal schnell ausprobiert, was cin.rdbuf()->in_avail() zurückgibt, einmal mit std::ios::sync_with_stdio(false) und einmal ohne. Wenn man es nicht setzt, dann ist rdbuf()->in_avail() bei meiner implementierung nach jedem eingelesenen Zeichen 0. Wenn man es setzt, ist rdbuf()->in_avail() nach jedem eingelesenen newline 0. Das muss aber nicht unbedingt so sein, der Standard ist an der Stelle nicht sehr exakt, es könnte sein, dass du eine Implementierung findest, die es genauso macht, wie du willst. Aber was nützt das, wenn man sich nicht drauf verlassen kannst?

    Und wie genau soll mir die Art dieser bei meinem Problem helfen?

    Eigentlich hilft es nicht, weil es so auch nicht funktioniert. Aber so kannst du dir sparen, diesen Weg auszuprobieren. Und noch eine andere Sache: Das sync_with_stdio legt fest, ob die C++ streams mit den C streams synchronisiert werden. Wenn sie es werden (was die Standardeinstellung ist), dann hast du oben beschriebenes Verhalten. Daher die Vermutung, dass dir auch die C streams nicht weiterhelfen werden.



  • Also ich hab jetzt mal VS08 angeschmissen und mir dein Problem man näher angeschaut, und zur folgenden Überzeugung gekommen (ohne Gewähr):

    Dein Problem ist nicht der cin-Buffer den musst du in jedem Fall c/p über die Konsole vollständig entgegennehmen da führt (AFIK) kein Weg vorbei, was eine Möglichkeit wäre (ich erinnere mich da noch DOS-Zeiten) das du Pipes anwendest, alles OS-Spezifisch natürlich, du wirst dieses Problem (AFIK) nicht mit der C++ Lib allein lösen können, da bleibt dir nur der Weg über die WinAPI oder was Systemspezifisches.

    Meiner Meinung nach versuchst du bei der Problemlösung den zweiten Schritt vor dem Ersten zu machen. 🙂

    Also ich würde folgendes Machen: Entkopple dein STDOUT-Pipe der Konsole mittels Win32API von der Konsole, verarbeite alle Eingaben von c/p, hänge dein STDOUT-Pipe der Konsole wieder auf die Konsole, und gib nur das was du willst aus.

    Das habe ich selbst schon erfolgreich angewendet, allerdings handelte es sich hierbei um den Commando-Interpreter, wenn du es Mittels std::ios doch noch schaffen solltest, lass ich mich bitte wissen, würde mich auch interessieren.

    Grüssle DC


Anmelden zum Antworten