Problem mit getchar

  • Themenstarter Themenstarter ByeBye 249714
  • Beginndatum Beginndatum
Waaa...ich hab diesen Thread komplett vergessen, tschuldigung :-(.

Kommt in Kürze...

Kennst du dich Dateien aus? fopen, fclose?
Wenn nicht, ist auch egal.
Jedenfalls kann man mit fopen eine Datei öffnen und bekommt eine FILE-Variable.
(fclose "schließt" diese Variable/Datei wieder).

Wenn man so eine FILE-Variable hat, kann man mit fgets aus der dazugehörenden Datei eine Zeile einlesen.
fgets kann aber nicht wie scanf mit %i, %d, %f etc umgehen, sondern liest die gesamte Zeile einfach Buchstabenweise ein.
(deswegen später das sscanf, um die Ziffern zu einer kompletten Zahl im int zu machen)

Wenn also fgets aus einer Datei etwas einliest, warum steht das dann da? Welche Datei?
Der Witz daran ist: In der Konsole werden die Tastatur und die Bildschirmausgabe auch als FILE-Variablen behandelt.
Die muss man nicht mit fopen/fclose öffnen oder schließen, die gibts einfach immer.
stdin (STanDard INput) ist dabei die Tastatur.
Das Gegenteil stdout gibt beim reinschreiben Buchstaben in die Konsole aus.

fgets will als ersten Parameter ein char-Array, zum Speichern der eingelesenen Zeile. Mein 24 Buchstaben langes str.
Der zweite Parameter ist die Maximallänge, die da reingespeichert werden darf. 24.
Wenn die Zeile länger als 24 Buchstaben ist, werden die restlichen ignoriert.
Der dritte Parameter ist die Datei bzw. FILE-Variable, aus der gelesen werden soll. stdin, damit die Eingabe von der Tastatur kommt.

Eine Eigenheit von fgets muss man noch erwähnen: Die Entertaste / der Zeilenwechsel am Schluss der Zeile wird beim Einlesen mit abgespeichert.
Und genau wie printf, bei dem man mit \n einen Zeilenwechsel machen kann, kann ich diesen auch als \n behandeln.
Wenn die Zeile jetzt aber länger als 24 war, bekomme ich nur die ersten 24 Zeichen in str.
Der Zeilenwechsel vom Ende der Zeile ist dann nicht mit in str dabei.

Ein weiteres "Problem": Wenn die Zeile länger als 24 war und ich nach diesem fgets wieder was von der Tastatur einlesen will, wird beim 25. Zeichen weitergemacht und nicht auf neue Tastendrucke gewartet. Der eventuell vorhandene abgeschnittene Zeilenrest muss also entfernt werden.

(!) Das ist auch genau das Problem, das du mit dem normalen scanf am Anfang gehabt hast.
Es lässt einfach einen Teil der Eingabe "hinten"; das getchar danach hat sich dann dort bedient, statt auf eine neue Benutzereingabe zu warten.
Wegen diesem Problem ist diese ganze Funktion erst nötig geworden.
Ich lasse fgets einlesen, entferne mit den drei noch nicht erklärten Zeilen alles Übriggebliebene und lasse sscanf aus meiner Buchstaben/Ziffern-Sammlung von fgets ein int rauslesen.

So, wie wird jetzt etwas Übriggebliebenes entfernt?

erg=strlen(str)-1;
Zuerst finde ich heraus, wieviel Buchstaben/Ziffern eingegeben wurden.
strlen(str) macht das automatisch.
Aber wie das in C/C++ so ist: Wenn 4 Buchstaben im str sind, sind sie in str[0], str[1], str[2] und str[3].
Bei strlen=4 ist der letzte Buchstabe also auf 3.
strlen(...)-1 also. Diese Nummer brauche ich,
Ich speichere das in erg, damit ich kein zweites int brauche. Die eingelesene Zahl (die mit return erg zurückgegeben wird) wird dadurch nicht geändert, weil das sscanf, das erg mit der richtigen Zahl füllt, ja erst danach kommt.

if(erg>=0&&str[erg]=='\n')str[erg]='\0';
Wenn überhaupt irgendwas eingegeben wurde (erg>=0 -> strlen-1 >=0 ->Min. 1 Buchstabe eingegeben) und der letzte "Buchstabe" der Zeilenwechsel von fgets ist: Das bedeutet, der Benutzer hat nicht mehr wie 24 Buchstaben eingegeben.
Der Zeilenwechsel ist noch in str, dh die komplette Zeile und nichts wurde von fgets abgeschnitten.
Dann markiere ich den jetztigen letzen Buchstaben als Stringende (und entferne somit den Zeilenwechsel, den braucht keiner).
Dann ist alles in Ordnung.

else{while(fgetc(stdin) != '\n');}
Wenn der letzte Buchstabe aber kein Zeilenwechsel war, ist noch irgendwo ein abgeschnittener Rest der Zeile, der das nächste scanf/getchar/was auch immer stören würde.

fgetc liest aus der angegeben Datei genau einen Buchstaben ein, nichts weiter.
Nicht ganz so wie getchar() bei der Tastatur, das liest einen Buchstaben und wartet danach aber noch auf ein Enter.
fgetc liest wirklich nur einen Buchstaben.
Und wenn man es mit stdin als FILE verwendet, genau einen Tastendruck von der Tastatur.

Wenn also noch ein Zeilenrest vorhanden ist, liest die whle-Schleife solange Einzelbuchstaben ein, bis ein Zeilenwechsel dabei ist.
Sonst tut die Schleife nichts mehr, deshalb der Strichpunkt statt {...} dahinter.
fgetc liest jetzt trotz stdin nicht wirklich Tasten ein, sondern hat das gleiche "Problem" wie alles andere: Es bedient sich bei dem übriggebliebenen Zeilenrest.
(In dem Fall ist das sogar gut, genau das soll es ja auch machen).
Es liest jetzt jeden übriggebliebenen Buchstaben ein, bis der Zeilenwechsel kommt. Wenn der auch noch weg ist, kann es aufhören, da die Zeile jetzt aus ist.

Damit wären alle Spuren des fgets beseitigt, alles perfekt. Den Rest der Geschichte kennst du.

...Wieder einmal ein Roman, den ich hier geschrieben habe. Lässt du mir eine Bewertung da? ;)

Gruß
 
Zuletzt bearbeitet:
Ja, natürlich - spitze, wie ausführlich du geantwortet hast. !!

Gibt es so eine Funktion nicht auch in einer Bibliothek? Das wundert mich gerade, müssten solche Situationen nicht öfter vorkommen?
 
"if(erg>=0&&str[erg]=='\n')str[erg]='\0';
Wenn überhaupt irgendwas eingegeben wurde (erg>=0 -> strlen-1 >=0 ->Min. 1 Buchstabe eingegeben) und der letzte "Buchstabe" der Zeilenwechsel von fgets ist: Das bedeutet, der Benutzer hat nicht mehr wie 24 Buchstaben eingegeben.
Der Zeilenwechsel ist noch in str, dh die komplette Zeile und nichts wurde von fgets abgeschnitten.
Dann markiere ich den jetztigen letzen Buchstaben als Stringende (und entferne somit den Zeilenwechsel, den braucht keiner)."

Angenommen ich habe "hi" eingegeben, dann ist erg=2-1=1
str[erg] kann doch nie == '\n' sein, da steht doch immer der letzte Buchstabe?
Müsste man nicht str[erg+2] schreiben? +1, dann wär man bei der binären Null und dann noch einen weiter, um zum Zeilenumbruch zu kommen
 
Nicht ganz.
Der Zeilenumbruch wird von strlen, fgets (etc) als regulärer Buchstabe betrachtet und steht VOR der binären Null.
Buchstaben und Index im char-Array:

h i \n \0
0 1 2 3

strlen ergibt 3, minus 1 ist zwei. Damit habe ich den Index vom Zeilenwechsel.

Gruß
 
Bei mir gibt strlen dann als Zeichenanzahl 2 an

Code:
#include <stdio.h>

int main()
{
  char satz[10];
  
  gets(satz);
  printf("Zeichenanzahl: %i", strlen(satz));
  system("PAUSE");	
  return 0;
}

benutze gets, bei fgets meckert er
 
Was wird denn gemeckert?
fgets ist deshalb vorzuziehen, weil man eine Längenbeschränkung hat.
Wenn man bei gets zuviel eingibt, gibts die lustigsten Probleme bis zum Programmabsturz.
 
Das wird nicht angezeigt bzw. ich finde nicht, wie man die Warnung erläutert einblenden könnte (benutze DEV-C++). Wenn ich den Warnhinweis ignoriere, bekomme ich aber trotzdem nur 2 ausgegeben

Edit: http://www.cplusplus.com/reference/clibrary/cstdio/gets/
"The ending newline character ('\n') is not included in the string."
Also str[erg+1]?
 
Zuletzt bearbeitet von einem Moderator:
Also bei mir kommt ordentlich 3 heraus.

Mit den Einstellungen von DevCpp kenn ich mich nicht aus.

Wenn du schreibst, du willst die Warnung "erläutert" einblenden, bedeutet das, dass du nur eine Nummer mit einem C vorne siehst? Immer her damit. Ich kenn zwar nicht alle auswendig (:D), aber es gibt ja noch Google.
 
Nein, die Zeile wird nur rot angemarkt :/
In meinem C Buch wird "\n" aber auch nie mit gezählt :|
 
Zurück