Forum: PC-Programmierung [Linux] Eingabepuffer leeren in C


von Christoph S. (mixer) Benutzerseite


Lesenswert?

Hallo,

da es den Befehl "fflush(stdin);" in Linux ja nicht gibt bzw. er keine 
Auswirkung hat suche ich jetzt eine Möglichkeit den Eingabepuffer zu 
leeren um mit scanf/getchar dann einzelne Zeichen bzw. Strings sauber 
einlesen kann.

Momentan arbeite ich mit
1
while( getchar() != '\n' )

Allerdings muss ich dann zweimal Enter drücken. Bei Windows mit der 
fflush-Funktion brauche ich beim gleichen Code nur ein Enter.

Gibt es eine einfachere/bessere Möglichkeit den Eingabepuffer komplett 
zu leeren??

MFG Mixer

von Klaus W. (mfgkw)


Lesenswert?

Was hast du vor?
Für etwas ähnliches wie ein Konsolenprogramm mit Menüsteuerung ist
die Standardeingabe eher ungeeignet.

von Christoph S. (mixer) Benutzerseite


Lesenswert?

Klaus Wachtler schrieb:
> Was hast du vor?
> Für etwas ähnliches wie ein Konsolenprogramm mit Menüsteuerung ist
> die Standardeingabe eher ungeeignet.

Sowas habe ich aber vor. Was soll ich dann stattdessen nehmen??

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

fflush() ist ISO C90. Warum sollte es diese Funktion ausgerechnet unter 
Linux nicht geben? fflush() schreibt alle _Ausgabe-Daten und duerfte 
deshalb keinen Effekt auf stdin haben.

Was du suchst ist fpurge(), das ist aber keine Standard-Funktion. Wenn 
es standard-konform sein muss, dann wuerde ich mir mit fileno() den 
filedescriptor holen und dann mit read() alle Daten lesen.
Um abzubrechen wenn keine daten mehr im File sind, wurder ich entweder 
select() nehmen oder den fd non-blocking setzen.

Ansonsten koenntest du eine Funktion von ncurses verwenden(?)

von Klaus W. (mfgkw)


Lesenswert?

Meine Lösung für solche Fälle ist _getch(). _kbhit() (conio.h
mit diesen Funktionen gibt es auch unter DOS/Windows); weil man
es für Quelltexte aus der Windowswelt öfter braucht, ist Sleep()
auch gleich mit dabei, auch wenn es dort nicht zu conio.h gehört.
Wer es also etwas eleganter will, wirft Sleep() hier raus und
packt sie in die richtige Headerdatei.

_kbhit() liefert true, wenn ein Zeichen geholt werden kann, ohne
zu blockieren.

_getch() liefert ein Zeichen, oder im Fehlerfall -1.

_putch() gibt ein Zeichen aus.

Sleep() wartet die angegebene Zahl msec.


Eine Headerdatei conio.h:
1
#ifndef __CONIO_H_
2
#define __CONIO_H_
3
4
//////////////////////////////////////////////////////////////////////
5
//
6
// Sleep(), _kbhit(), und _getch() fuer __unix__.
7
//
8
9
10
#include <stdlib.h>
11
#include <stddef.h>
12
#include <stdio.h>
13
#include <string.h>
14
15
#include <termios.h>
16
#include <unistd.h>
17
18
#include <sys/select.h>
19
#include <sys/types.h>
20
21
22
23
/* Sleep() wartet die angegebene Anzahl Millisekunden (im Gegensatz
24
 * zu ANSI-sleep(), das kennt nur ganze Sekunden).
25
 */
26
void Sleep( int msec );
27
28
// _kbhit() liefert true, wenn mindestens ein Zeichen im
29
// Tastaturpuffer steht
30
int _kbhit( void );
31
32
// liefert ein Zeichen von der Tastatur (oder EOF bei Fehler)
33
int _getch( void );
34
35
// gibt ein Zeichen aus
36
void _putch( char c );
37
38
//
39
// Ende von Sleep(), _kbhit(), und _getch() fuer __unix__.
40
//
41
//////////////////////////////////////////////////////////////////////
42
43
#endif /* ifndef __CONIO_H_ */

Quelltext conio.c jeweils mitkompilieren:
1
//////////////////////////////////////////////////////////////////////
2
//
3
// Sleep(), _kbhit(), und _getch() fuer __unix__.
4
//
5
6
#include "conio.h"
7
8
9
/* Sleep() wartet die angegebene Anzahl Millisekunden (im Gegensatz
10
 * zu ANSI-sleep(), das kennt nur ganze Sekunden).
11
 */
12
void Sleep( int msec )
13
{
14
  struct timeval
15
    timeout;
16
17
  timeout.tv_sec  = msec/1000;
18
  timeout.tv_usec = msec%1000;
19
20
  select( 0, NULL, NULL, NULL, &timeout );
21
}
22
23
// In einer struct termios wird eine Terminalkonfiguration
24
// gespeichert.
25
// Davon brauchen wir zwei: eine fuer den Originalzustand,
26
// und eine zum Aendern.
27
static struct termios     term_neu,
28
                          term_original;
29
30
// Darin wird die Aenderung der Terminaleinstellungen gemerkt,
31
// damit sie nur einmal stattfinden:
32
static int                _conio_initialisiert = 0;
33
34
// Bei Programmende soll automatisch die Originalkonfiguration
35
// wiederhergestellt werden:
36
static void _exit_conio( void )
37
{
38
  if( _conio_initialisiert )
39
    {
40
      tcsetattr( 0, TCSANOW, &term_original );
41
      _conio_initialisiert = 0;
42
    }
43
}
44
45
// Diese Funktion macht beim ersten Aufruf folgendes:
46
// - Merken der Originalkonfiguration von stdin (file id=0)
47
// - Installieren von _exit_conio() mit atexit()
48
// Bei jedem Aufruf zudem:
49
// - Ausschalten von ECHO und ICANON (u.a. Zeilenpufferung)
50
static void _init_conio( void )
51
{
52
  if( !_conio_initialisiert )
53
    {
54
      //  Dies passiert nur beim ersten Aufruf
55
56
      // Originalkonfiguration holen:
57
      tcgetattr( 0, &term_original );  // 0 ist stdin
58
      // komplett kopieren:
59
      term_neu = term_original;
60
      // ICANON und ECHO ausschalten:
61
      term_neu.c_lflag = term_neu.c_lflag & ~(ICANON | ECHO);
62
      // Bei Programmende wieder aufraeumen lassen:
63
      atexit( _exit_conio );
64
      // In _conio_initialisiert merken, dass wir schon etwas getan
65
      // haben:
66
      _conio_initialisiert++;
67
    }
68
69
  // bei jedem Aufruf die eigene Terminalkonfiguration
70
  // fuer stdin (file id = 0) passend setzen:
71
  tcsetattr( 0, TCSANOW, &term_neu );
72
}
73
74
// _kbhit() liefert true, wenn mindestens ein Zeichen im
75
// Tastaturpuffer steht
76
int _kbhit( void )
77
{
78
  fd_set
79
    lesen_set;
80
81
  struct timeval
82
    timeout;
83
84
  _init_conio();
85
86
  FD_ZERO( &lesen_set );
87
  FD_SET( 0, &lesen_set ); // 0 ist std-Eingabe
88
89
  timeout.tv_sec  = 0;
90
  timeout.tv_usec = 0;
91
92
  if( select( 1,
93
              &lesen_set,  // set fuer "Lesen"
94
              NULL,        // kein set fuer "Schreiben"
95
              NULL,        // kein set fuer "out of band data"
96
              &timeout
97
              )
98
      ==-1
99
      )
100
      {
101
        // select macht Bloedsinn!
102
        _exit_conio();
103
        return 0;
104
      }
105
  else
106
    {
107
      // in lesen_set steht jetzt an der Stelle [0], ob gelesen
108
      // werden kann:
109
        _exit_conio();
110
      return FD_ISSET( 0, &lesen_set );
111
    }
112
}
113
114
// liefert ein Zeichen von der Tastatur (oder EOF bei Fehler)
115
int _getch( void )
116
{
117
  char c;
118
  int ret;
119
120
  _init_conio();
121
122
  ret = read( 0, &c, 1 );
123
124
  _exit_conio();
125
  return ( ret==1 ) ? (int)(unsigned char)c : EOF;
126
}
127
128
// gibt ein Zeichen aus
129
void _putch( char c )
130
{
131
  write( 1, &c, 1 );
132
}
133
134
135
//
136
// Ende von Sleep(), _kbhit(), und _getch() fuer __unix__.
137
//
138
//////////////////////////////////////////////////////////////////////

Beispielprogramm: liest Zeichen ein und gibt jeweils ihrem ASCII-Wert
aus, bis man auf Esc drückt:
1
#include "projekte/conio.h"
2
3
4
int main( int nargs, char **args )
5
{
6
  int c;
7
  char puffer[100];
8
  int iZeichen;
9
10
  do
11
  {
12
    c = _getch();
13
    sprintf( puffer, "das Zeichen ist dezimal %d", c );
14
    for( iZeichen=0; iZeichen<sizeof(puffer)&&puffer[iZeichen]; ++iZeichen )
15
    {
16
      _putch( puffer[iZeichen] );
17
    }
18
    _putch( '\n' );
19
  }
20
  while( c!=0x1b );
21
22
  return 0;
23
}

von Klaus W. (mfgkw)


Lesenswert?

PS:
Das Ganze funktioniert in einem Konsolenfenster (xterm etc.)
ebenso wie auf der Textkonsole.

von Christoph S. (mixer) Benutzerseite


Lesenswert?

Hallo,

Danke für eure Tips!!

Habs jetzt mal schnell mit
1
__fpurge(stdin);
probiert und es sieht gut aus!!

MFG Mixer

von Rolf Magnus (Gast)


Lesenswert?

Wieso wollen alle eigentlich immer fflush dafür mißbrauchen? fflush 
dient dazu, den Inhalt des Puffers an sein Ziel zu schreiben  (und nicht 
etwa zu verwerfen, wie viele zu glauben scheinen) , was natürlich nur 
für Ausgangspuffer einen Sinn ergibt.

von Simon K. (simon) Benutzerseite


Lesenswert?

Deswegen hat es ja bei stdin auch keinen Sinn. So wie ich das verstanden 
habe.

Hatte aber letztens das Problem, dass ich perror vor einem printf 
aufgerufen hab und der Text von perror stand NACH dem von printf. Da 
half nur ein fflush(stdout).
Verstehe aber selber nicht so genau warum das in dem Fall verdreht wird. 
Wusste nur, dass man solche Phänomenen damit entgegnen kann.

von Rolf Magnus (Gast)


Lesenswert?

> Deswegen hat es ja bei stdin auch keinen Sinn. So wie ich das verstanden
> habe.

Richtig.

> Hatte aber letztens das Problem, dass ich perror vor einem printf
> aufgerufen hab und der Text von perror stand NACH dem von printf.

War's nicht anders herum? Eigentlich sollte perror (stderr) ungepuffert 
direkt ans Terminal raus, während stdout puffert und der Text daher 
evtl. erst später ankommt.

von Klaus W. (mfgkw)


Lesenswert?

nicht andersrum?
erst printf aufgerufen dann perror, aber Ausgabe in der anderen 
Reihenfolge?

von Simon K. (simon) Benutzerseite


Lesenswert?

War eine Schleife, also ist quasi vor dem perror auch nach dem perror. 
Kann also sein, dass es auch mit den printfs davor zusammenhing. :-)
Aber warum ist perror nicht der Einfachheit halber auch gepuffert? Ist 
doch blöd wenn man sich da noch als Anwender einer API um 
Puffersynchronisierung kümmern muss.

von Karl H. (kbuchegg)


Lesenswert?

Simon K. schrieb:
> War eine Schleife, also ist quasi vor dem perror auch nach dem perror.
> Kann also sein, dass es auch mit den printfs davor zusammenhing. :-)
> Aber warum ist perror nicht der Einfachheit halber auch gepuffert?

Weil es erlaubt ist, dass eine normale Ausgabe durchaus auch eine halbe 
Stunde im Ausgabebuffer steht.
Bei einer Fehlermeldung ist es aber unter Umständen mehr als fatal, wenn 
sie durch das I/O System verzögert wird.

von Simon K. (simon) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Simon K. schrieb:
>> War eine Schleife, also ist quasi vor dem perror auch nach dem perror.
>> Kann also sein, dass es auch mit den printfs davor zusammenhing. :-)
>> Aber warum ist perror nicht der Einfachheit halber auch gepuffert?
>
> Weil es erlaubt ist, dass eine normale Ausgabe durchaus auch eine halbe
> Stunde im Ausgabebuffer steht.
> Bei einer Fehlermeldung ist es aber unter Umständen mehr als fatal, wenn
> sie durch das I/O System verzögert wird.

Halbe Stunde? Ich dachte der Sinn von dem Puffer in dem Fall ist, dass 
immer gesammelt wird und spätestens alle X Millisekunden das als ein 
Haufen an das System übermittelt wird um den Aufruf Overhead zu 
verringern. Bzw das Verhältnis von Daten zu Overhead zur erhöhen.

von Klaus W. (mfgkw)


Lesenswert?

Es wird nicht alle soundsoviel msec ausgegeben, sondern defaultmäßig
immer bei einem \n.

von Karl H. (kbuchegg)


Lesenswert?

Simon K. schrieb:
> Halbe Stunde? Ich dachte der Sinn von dem Puffer in dem Fall ist, dass
> immer gesammelt wird und spätestens alle X Millisekunden das als ein
> Haufen an das System übermittelt wird

Wie soll das auf einem Nicht-Multitasking System gehen?

Üblich ist, dass der Buffer bei einem bestimmten Füllgrad bzw beim 
Auftreten eines \n geleert wird (oder durch einen fflush)
Wenn ich 2 Zeichen (ohne \n) in den Buffer stelle und dann eine halbe 
Stunde keinen printf mache, dann können die beiden Zeichen durchaus eine 
halbe Stunde im Ausgabebuffer warten, bis ich sie dann endlich mit einem 
\n erlöse.

von Rolf Magnus (Gast)


Lesenswert?

> Aber warum ist perror nicht der Einfachheit halber auch gepuffert?

Es ist nicht speziell perror, sondern generell alles, was nach stderr 
geht. Das wird deshalb gemacht, weil dahin oft eben Debug- oder 
Fehlerausgaben geschrieben werden, bevor ein Programm abschmiert, und 
dann will man, daß diese Ausgaben auch sicher ankommen und nicht 
irgendwo im Puffer versickern, denn der verschwindet zusammen mit dem 
Programm.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.