C Fragen zu Eingabestrom und Einlesen aus der Tastatur

dragonabllz

Ensign
Registriert
Mai 2011
Beiträge
254
Hallo

ich stehe vor einem Problem in C, das ich nicht ganz verstehe (bzw. die internen Vorgänge nicht nachvollziehen kann).
Im Folgenden ist ein sinnloser Code, der nur das Problem kenntlich machen soll:

Code:
void sinnlos(){ 
    char input[4];  // Zwei Zeichen + '\n' + '\0' 
  
    fgets(input,4,stdin); 
  
    if((input[0]<='8' && input[0]>='1') && (input[1]=='X')){ 
        printf("\nToll gemacht!"); 
    else if(input[0] == '\n') 
        exit(-1); 
    else 
        printf("\nFalsche Eingabe"); 
    
    deleteBuffer(); 
    sinnlos(); 
  
  
void deleteBuffer(){ 
    char c; 
    
    do{ 
        c=getchar(); 
    }while(c!='\n');

Also das (sinnlose) Programm verlangt eine Zahl zwischen 1-8 und dahinter ein X (Sinn und Zweck ist hier erstmal nicht vorhanden!!!). Das Programm ruft sich auch immer wieder selber auf.
Gibt man irgendwas falsches an, z.b: "blabla" dann wird "Falsche Eingabe" ausgegeben und die Funktion wird wieder aufgerufen.

Jetzt kommen wir mal zum Problem:
Gebe ich "8Xabcde" ein ist alles kein Problem. Der Code nimmt das "8X" auf und löscht den Rest aus dem Puffer bzw. aus stdin(?)
mithilfe von deleteBuffer, denn sonst würde beim nächsten Aufruf von "fgets" das "abc" kopiert.
Gebe ich aber z.b. nur "5" oder eine richtige Eingabe z.b. "6X" ein, dann hängt das Programm bei deleteBuffer und man muss aufgrund der Zeile "c=getchar()" ein Enter drücken.
An sich kein Weltuntergang, dennoch möchte ich das vermeiden. Und überhaupt verstehe ich nicht weshalb das geschieht.
Bei Eingaben > 2 geschieht dies nicht. Nur bei Eingaben <= 2. Ich weiss, dass dies mit fgets und getchar und stdin zusammenhängt,
weiß aber nicht wo genau das Problem liegt.

Würde mich über jegliche Hilfe freuen.


PS: Eine Nebenfrage: Kann man anders als mit "deleteBuffer" den Eingabestrom (stdin) löschen? Mit fflush() funktioniert das nicht.
 
Es geht mir einfach nur um den Umgang mit Eingabestrom und Puffer leeren.
Der obige Code sollte reichen.
In main einfach noch hinschreiben:

Code:
int main(){
     sinnlos();
return 1;
}

FERTIG!
 
Das Problem ist das fgets bis zu 4 Zeichen aus dem Eingabebuffer entnimmt. Wenn in diesen bereits das '\n' enthalten war so führt dies dazu dass die Schleife in der Funktion deleteBuffer() solange läuft bis ein zweites Return auftaucht (das erste ist ja schon "weg")... das du aber bisher nicht eingegeben hast.

Eine andere Funktion zum Buffer löschen wüsst ich jetzt aus dem Stehgreif auch nicht. Ist zu lang her das ich mit C Kontakt hatte...
 
Dein Programm wird sich übrigens sowieso in jedem Fall abschießen da es sich rekursiv selbst aufruft. Stack Overflow.
Mach das besser aus der main() mit einem while(true){ sinnlos(); }
 
Jesterfox schrieb:
Das Problem ist das fgets bis zu 4 Zeichen aus dem Eingabebuffer entnimmt. Wenn in diesen bereits das '\n' enthalten war so führt dies dazu dass die Schleife in der Funktion deleteBuffer() solange läuft bis ein zweites Return auftaucht (das erste ist ja schon "weg")... das du aber bisher nicht eingegeben hast.

Eine andere Funktion zum Buffer löschen wüsst ich jetzt aus dem Stehgreif auch nicht. Ist zu lang her das ich mit C Kontakt hatte...

Hmmm... ok ich verstehe das Problem. Weiß aber nicht wie ich das lösen kann. Ich schau mir erstmal den Link von @asdfman an
Ergänzung ()

calav3ra_de schrieb:
Dein Programm wird sich übrigens sowieso in jedem Fall abschießen da es sich rekursiv selbst aufruft. Stack Overflow.
Mach das besser aus der main() mit einem while(true){ sinnlos(); }

Ok danke schön ;)
 
dragonabllz schrieb:
Es geht mir einfach nur um den Umgang mit Eingabestrom und Puffer leeren.

Die Lösung ist einfach: Sorge dafür, dass das Problem nicht auftritt. Ein meiner Meinung nach sinnvoller Ansatz dafür:
Lies die Eingabe des Benutzers vollständig ein und benutze nur den Teil, den du benötigst. Wenn die Eingabe von der
Spezifikation abweicht, tadele den Benutzer, was er doch für ein böser Junge ist.
 
"Komplett einlesen" hat immer so das Problem mit der unbekannten Größe des benötigten Buffers... der Ansatz des TE ist daher schon zielführend und der Link den asdfman gepostet hat zeigt was man verbessern muss: die Schleife in deleteBuffer) muss nicht nur auf '\n' sondern auch auf EOF prüfen.
 
calav3ra_de schrieb:
Dein Programm wird sich übrigens sowieso in jedem Fall abschießen da es sich rekursiv selbst aufruft. Stack Overflow.

Das ist zwar theoretisch richtig und man sollte es deshalb vermeiden. In der echten Welt gibt es aber Tail Call Optimization.
 
"Komplett einlesen" hat immer so das Problem mit der unbekannten Größe des benötigten Buffers... der Ansatz des TE ist daher schon zielführend und der Link den asdfman gepostet hat zeigt was man verbessern muss: die Schleife in deleteBuffer) muss nicht nur auf '\n' sondern auch auf EOF prüfen.

Funktioniert leider nicht. Immer noch selbe Problem trotz richtigem deleteBuffer:

Code:
void deleteBuffer(){
	char c;
	do{
		c = getchar();
	} while((c != '\n') && (c!=EOF));
}
 
Jesterfox schrieb:
"Komplett einlesen" hat immer so das Problem mit der unbekannten Größe des benötigten Buffers.

Buffer Overflows sind doch nur dann ein Problem, wenn man sie eben nicht erwartet. Hier drängt es sich förmlich auf und die Lösung ist trivial.
 
asdfman schrieb:
Buffer Overflows sind doch nur dann ein Problem, wenn man sie eben nicht erwartet. Hier drängt es sich förmlich auf und die Lösung ist trivial.

Aber der naive User kann einen Buffer Overflow herbeiführen durch große Eingaben...
Ich frage mich weshalb die Lösung mit EOF nicht funktionieren will
 
dragonabllz schrieb:
Aber der naive User kann einen Buffer Overflow herbeiführen durch große Eingaben...
Ich frage mich weshalb die Lösung mit EOF nicht funktionieren will

Das hast du falsch verstanden. Es ist klar, dass man nicht vorhersehen kann, was der User eingibt, weshalb man
die Eingabe auf eine Art einliest, die Buffer Overflows vermeidet. Moment ich C/P mal was aus einem beliebigen
Projekt von mir, das Benutzereingaben annimmt.

[..................] AH! Da ist es ja!

Code:
char *get_line(FILE *fp) {
	size_t len = 0, size = 0;
	char *buf  = NULL;

	do {
		size += MAXBUF;
		buf = realloc(buf, size);
		fgets(buf + len, MAXBUF, fp);
		len = strlen(buf);
	} while (!feof(fp) && buf[len - 1]!='\n');
	buf[len - 1] = '\0';

	return buf;
}

€: Der Autist in mir bemerkt, dass man das natürlich für nicht-strings verallgemeinern sollte. Aber für deine Zwecke
sollte es eigentlich passend sein.
 
Zuletzt bearbeitet:
Als Programmieranfänger verstehe ich deinen Code zwar grad nicht... aber ich lese mich da mal ein.
Danke
 
Es liest die Eingabe in Stückchen und vergrößert den Puffer bei Bedarf. Ich kann es ja mal durchgehen.

Die Funktion benötigt eine Konstante MAXBUF, die angibt, wie groß die Stückchen sein sollen, die gelesen
werden. Erstmal ist nicht so wichtig, wie groß das ist. Nimm einen Wert in der Größenordnung der Länge der
zu erwartenden Eingabe.

Die Variable len bekommt den Wert, der bisher insgesamt eingelesen wurde und wird mit 0 initialisiert.
Die Variable size enthält die Größe des Puffers.
Die Variable buf enthält den Puffer und wird mit dem Nullzeiger initialisiert, um realloc() beim ersten
Aufruf zu signalisieren, dass noch kein Speicher für ihn reserviert ist.

Es folgt die einlesende Schleife:
Zuerst wird size um MAXBUF vergrößert und neuer Speicher für buf angefordert, der Platz für size
Bytes hat. Hier möchte ich bemerken, dass realloc() fehlschlagen kann und dann einen Nullzeiger zurückgibt.
Darauf sollte geprüft werden, wird aber hier nicht gemacht. Sowas solltest du dir nicht angewöhnen.

Dann werden MAXBUF Bytes aus dem Stream gelesen, der der Funktion als Parameter übergeben wurde. Diese
werden len Bytes nach dem Anfang des Puffers in den Speicher geschrieben.

Zuletzt wird mit strlen die Länge des gelesenen Strings ermittelt, der kleiner sein kann, als die Größe des
Puffers, weil der Benutzer ja mittendrin enter gedrückt haben könnte.

Die Schleife wird so lange wiederholt, bis entweder EOF oder Newline ankommt.

Zuletzt wird das Newline mit '\0' überschrieben und damit aus dem String entfernt.
 
Zuletzt bearbeitet:
Dass ich mir den ganzen Wolf für nichts geschrieben habe enttäuscht mich jetzt irgendwie :(
 
Zurück
Oben