pelzfrucht
Goto Top

Scanf bzw. getchar wird übersprungen. Was tun?

Abend,

tut mir leid erstmal -- Ist bestimmt eine totale Anfänger frage, aber ich bin Anfänger und hab da ein kleines Problem:

Bei diesem simplen Code:

#include <stdio.h>

main() {
    
    char menue_eingabe;
    char menue_eingabe2;
    
    printf("\nEingabe 1:  ");  
    scanf("%c", &menue_eingabe);  
    
    printf("\nEingabe 2:  ");  
    scanf("%c", &menue_eingabe2);  
    
    printf("\n\n==============================\n\nEingabe 1 ==>  %c\nEingabe 2 ==>  %c\n\n", menue_eingabe, menue_eingabe2);  
    
    return 0;
}

Wird nur Eingabe 1 abgefragt.
Das zweite scanf (Eingabe 2) wird dabei allerdings übersprungen.

Ausgabe:


Eingabe 1: B

Eingabe 2:


Eingabe 1 ==> B
Eingabe 2 ==>


Program ended with exit code: 0

Genau:

"Ich betätige die Taste B und drücke Enter".

Nach meiner Internet Recherche liegt dass wohl dadran, dass
scanf das "B" einliest und der Variable zuweist. Das Enter allerdings im Einlese Speicher liegen lässt. Das nächst scanf liest diesen dann aus und macht sofort weiter da er noch das "\n" von der vorherigen Eingabe vorfindet.

Kann mir bitte jemand weiterhelfen dieses simple Problem zu lösen?
Im Grunde sollen halt beide scanf ausgeführt werden. Das zweite wird ja wegen dem Enter vom vorherigen Befehl übersprungen.

Danke im Vorraus und Viele Grüße
pelzfrucht

PS: Mit getchar(); besteht das gleiche Problem.

Content-Key: 297833

Url: https://administrator.de/contentid/297833

Printed on: April 26, 2024 at 14:04 o'clock

Member: Lochkartenstanzer
Solution Lochkartenstanzer Mar 01, 2016, updated at Mar 02, 2016 at 22:41:06 (UTC)
Goto Top
Moin,

Du hast da glaubeich einen Denkfehler:

Dadruch, daß Du nach dem B auf enter drückst, hast Du schon mehrere zeichen eingegeben. Das erste Zeichen ist das B, das das erste scanf abbekommt. Das zweite zeichen bekommt das "Enter" ab. Wenn Du da etwas eingeben willst, mußt Du zuerst den Puffer leeren, bevor Du das scanf losläßt. Wenn Du das sehen wilst, gibt die zeichen statt als Character als Zahl aus.

lks

Nachtrag:
#include <stdio.h>

main() {
    
    char menue_eingabe;
    char menue_eingabe2;
    
    printf("\nEingabe 1:  ");
    scanf("%c", &menue_eingabe);
    
    printf("\nEingabe 2:  ");
    scanf("%c", &menue_eingabe2);
    
    printf("\n\n==============================\n\nEingabe 1 ==>  %i\nEingabe 2 ==>  %i\n\n", (int) menue_eingabe, (int) menue_eingabe2);

    
    return 0;
}

ergibt:

Eingabe 1:  a

Eingabe 2:  

==============================

Eingabe 1 ==>  97
Eingabe 2 ==>  10


Nachtrag: Du könntest einfach per while und scanf einfach den Puffer leeren, bevor Du die zweite Ausgabe machst. den Einzeiler überlasse ich Dir als Übung. face-smile
Mitglied: 114757
Solution 114757 Mar 01, 2016, updated at Mar 02, 2016 at 22:39:08 (UTC)
Goto Top
Moin,
schreib einfach beide scanf Zeilen so das du vor dem %c noch ein Leerzeichen setzt:
scanf(" %c", &menue_eingabe);   
Dann werden nicht sichtbare Zeichen bei der Eingabe ignoriert und nicht übernommen.

Oder Alternativ noch zwischen den beiden Abfragen noch ein getchar() setzen das die überflüssigen Zeichen konsumiert/aufnimmt.

Gruß jodel32
Member: rubberman
Solution rubberman Mar 01, 2016, updated at Mar 02, 2016 at 22:39:04 (UTC)
Goto Top
Hallo pelzfrucht.

Ja, das '\n' bleibt im Eingabepuffer, korrekt. Noch verrückter wird es wenn du mehrere Zeichen eingegeben hast, denn dann bleibt nicht nur das '\n' sondern auch die vorherigen Zeichen im stdin stehen und warten darauf gelesen zu werden. Im Normalfall würdest du jetzt mit einer while Schleife die überflüssigen Zeichen ignorieren, beim Einlesen eines char musst du aber den Fall, dass nur Enter gedrückt wurde, ohne eine Eingabe zu tätigen, auch noch berücksichtigen.
#include <stdio.h>

int main(void)
{
  int menue_eingabe = 0, menue_eingabe2 = 0;

  printf("\nEingabe 1:  ");  
  menue_eingabe = getchar();
  while (menue_eingabe != '\n' && getchar() != '\n');  

  printf("\nEingabe 2:  ");  
  menue_eingabe2 = getchar();
  while (menue_eingabe2 != '\n' && getchar() != '\n');  

  printf("\n\n==============================\n\nEingabe 1 ==>  %c\nEingabe 2 ==>  %c\n\n", menue_eingabe, menue_eingabe2);  

  return 0;
}
Grüße
rubberman
Member: pelzfrucht
pelzfrucht Mar 02, 2016 updated at 00:19:12 (UTC)
Goto Top
Danke euch für die Antworten face-smile

@Lochkartenstanzer und @rubberman

Nachtrag: Du könntest einfach per while und scanf einfach den Puffer leeren, bevor Du die zweite Ausgabe machst. den Einzeiler überlasse ich Dir als Übung.

Ich hab da ne Idee gehabt, die funktioniert leider nicht face-sad
while (getchar() != EOF) { getchar(); }

Quasi:

Wenn getchar NICHT leer (am Ende) dann: Unbenutze Zeichen mit "getchar();" aufsammeln.
Das hier von rubberman funktioniert prima, aber ich würde gerne verstehen...
while (getchar() != '\n' && getchar() != EOF){ getchar(); }  

warum != '\n'

Vielleicht könnt ihr mir da noch helfen face-smile

[Edit] Ich Depp.
beim Einlesen eines char musst du aber den Fall, dass nur Enter gedrückt wurde, ohne eine Eingabe zu tätigen, auch noch berücksichtigen.
da hast du es mir ja schon gesagt rubberman. Aber wenn man das "Enter ohne Eingabe" Szenario rauslässt, müsste die gleiche Schleife nur mit dem "Nicht EOF" Vergleich trotzdem funktionieren?
Warum tut es das nicht? [/Edit]
schreib einfach beide scanf Zeilen so das du vor dem %c noch ein Leerzeichen setzt:

scanf(" %c", &menue_eingabe);

Dann werden nicht sichtbare Zeichen bei der Eingabe ignoriert und nicht übernommen.

Yay, der Tipp ist gold wert face-big-smile
Ich wäre nicht auf die Idee gekommen dass das simple Leerzeichen quasi die Lösung für den Fall in scanf ist.
Gibts etwas ähnlich simples für "getchar();" ?

Danke und Viele Grüße
pelzfrucht
Member: rubberman
rubberman Mar 02, 2016 updated at 01:10:28 (UTC)
Goto Top
warum != '\n'
Im Schleifenkopf steht die Bedingung. Ist sie WAHR, wird die Schleife abgearbeitet, ist sie FALSCH, wird die Schleife abgebrochen. Bedeutet für das Beispiel, sobald das '\n' gelesen wurde, wird die Schleife abgebrochen.

Yay, der Tipp ist gold wert
Jein. Für deine Anforderung, nur das '\n' als 2. Zeichen zu ignorieren, ja. Aber: wenn der Benutzer fälschlicherweise mehrere Zeichen eingegeben hat, schlägt die Methode fehl (und Benutzer machen so etwas üblicherweise face-wink).

Gibts etwas ähnlich simples für "getchar();" ?
Nein.

Aber wenn man das "Enter ohne Eingabe" Szenario rauslässt, müsste die gleiche Schleife nur mit dem "Nicht EOF" Vergleich trotzdem funktionieren?
Warum tut es das nicht?
Weil es kein EOF im stdin gibt. Auch in dem Fall musst du prüfen, ob das '\n' gelesen wurde. Lediglich der erste Teil (Prüfung ob bereits die Variable ein '\n' beinhaltet) würde entfallen.

Grüße
rubberman
Member: pelzfrucht
pelzfrucht Mar 02, 2016 updated at 00:39:44 (UTC)
Goto Top
Stimmt, macht natürlich Sinn.

Logische Denken ist etwas eingeschränkt wegen der Uhrzeit, weshalb ich mir auch so schwer tue dass zu verstehen.
Ich lies mir deine Antwort(en) morgen genau durch und melde mich dann.

Eine Frage für heute noch:

Wenn es kein EOF im stdin gibt, kann " != EOF " ja im Grunde niemals falsch (bzw. könnte == EOF niemals wahr) werden, warum dann nicht den Teil rauslassen?

Sorry, aber ich gebe mir Mühe es zu verstehen, weil es blöd ist, etwas zu lernen ohne zu wissen wie es funktioniert face-smile

Viele Grüße, Angenehme Nacht
pelzfrucht
Member: rubberman
rubberman Mar 02, 2016 at 00:53:36 (UTC)
Goto Top
Wenn es kein EOF im stdin gibt
Hehe, das ist etwas schwierig zu verstehen. EOF wird nie erreicht, also wird getchar() erneut aufgerufen. Solange noch Zeichen im stdin liegen, werden diese ausgelesen. Liegen keine mehr im stdin, wartet getchar() auf eine Benutzereingabe. Wie du siehst, landest du so in einer Endlosschleife, die abwechselnd das '\n' aus der vorherigen Eingabe weg liest, um bei der nächsten Schleifeniteration wieder auf eine Benutzereingabe zu warten.

Grüße
rubberman
Member: Lochkartenstanzer
Lochkartenstanzer Mar 02, 2016 at 05:35:45 (UTC)
Goto Top
Zitat von @rubberman:

Aber wenn man das "Enter ohne Eingabe" Szenario rauslässt, müsste die gleiche Schleife nur mit dem "Nicht EOF" Vergleich trotzdem funktionieren?
Warum tut es das nicht?
Weil es kein EOF im stdin gibt.

Stimmt so nicht.

Natürlich gibt es ein EOF in stdin. Nämlich dann, wenn der User Ctrl-D drückt, oder wenn stdin aus einer Datei kommt, die Datei zu Ende ist.

Das Konstrukt des TO hat halt auf das Dateiende statt Zeilenende gewartet. face-smile


lks
Member: rubberman
rubberman Mar 02, 2016 at 07:23:24 (UTC)
Goto Top
Hallo lks.

wenn der User Ctrl-D drückt
Davon hab ich schon gehört. (Sorry bin auf Windows unterwegs ... face-wink)

oder wenn stdin aus einer Datei kommt
Wir reden von einer Benutzereingabe.

Aber ja, grundsätzlich alles gültige Einwände, nur hier nicht wirklich relevant.

Grüße
rubberman
Member: Lochkartenstanzer
Lochkartenstanzer Mar 02, 2016 updated at 08:02:36 (UTC)
Goto Top
Zitat von @rubberman:

Hallo lks.

wenn der User Ctrl-D drückt
Davon hab ich schon gehört. (Sorry bin auf Windows unterwegs ... face-wink)

Ctrl-Z?


oder wenn stdin aus einer Datei kommt
Wir reden von einer Benutzereingabe.

Aber ja, grundsätzlich alles gültige Einwände, nur hier nicht wirklich relevant.

Doch - das ist hiier relevant, weil der User das Ding irgendwann vielleicht ein Programm mit diesen Kenntnissen erstellt und dann jemand anders das Ding mit Eingabedaten aus einer Pipe oder Eingabeumleitung füttert. Das ist dann der Stoff aus dem exploits gemacht werden.

lks
Member: rubberman
rubberman Mar 02, 2016 updated at 17:08:03 (UTC)
Goto Top
Hallo lks.

Ctrl-Z?
Yep face-smile

Doch - das ist hiier relevant
Naja, da steht was von menue_eingabe. Da wäre jetzt eine Umleitung aus einer Datei nicht gerade das erste, was mir einfallen würde face-surprise Aber OK, auch da kann man sich noch absichern.
while (menue_eingabe != '\n' && menue_eingabe != EOF && getchar() != '\n' && !feof(stdin));

getchar() soll ja schließlich nicht umsonst ein int zurückgeben ...
Einverstanden? face-wink

Grüße
rubberman
Member: Lochkartenstanzer
Lochkartenstanzer Mar 02, 2016 at 17:16:17 (UTC)
Goto Top
Zitat von @rubberman:

Naja, da steht was von menue_eingabe. Da wäre jetzt eine Umleitung aus einer Datei nicht gerade das erste, was mir einfallen würde face-surprise

Da hast Du aber bisher selten "Menüprogramme" für automatisches scripting verhackwurstelt.

Irgendwann kommt garantiert der Tag, an dem ein Schlippssträger den einfall hat, so ein Programm automatisiert haben zu wollen udn das möglichst vorgestern, weil man damit den Affen vor der tastatur sparen kann, der immer da sgleiche eintippt. Und da der ursprüngliche Programmierer nicht mehr greifbar ist, wird halt schnell irgendetwas zusammengestümpert, daß das Programm mit einer Eingabedatei füttert. und schon hat man eine wudnerscjöne Vorlage für einen exploit. face-smile

lks
Member: rubberman
rubberman Mar 02, 2016 at 18:05:09 (UTC)
Goto Top
Da hast Du aber bisher selten "Menüprogramme" für automatisches scripting verhackwurstelt.
Hehe, um ehrlich zu sein kenne ich nicht ein einziges menügeführtes, professionelles Konsole-Tool. In der *nixoiden Welt mag das aber anders sein.

Irgendwann kommt garantiert der Tag, an dem ein Schlippssträger den einfall hat, so ein Programm automatisiert haben zu wollen udn das möglichst vorgestern, weil man damit den Affen vor der tastatur sparen kann...
Der verdient es dann aber auch, dass das in die Hose geht, oder? face-big-smile

Grüße
rubberman
Member: pelzfrucht
pelzfrucht Mar 02, 2016 at 19:58:35 (UTC)
Goto Top
Hey,

Danke dass ihr mir helft! face-smile

Für mich nochmal zum mitschreiben:

while (getchar() != '\n'  && getchar() != EOF) { getchar(); }  

Wenn stdin NICHT \n
UND
Wenn stdin NICHT Datei Ende
DANN
Ein Zeichen rausnehmen.
Wiederholt sich solange bis beide Bedingungen falsch werden.

Heißt also:

Wenn der Eingabepuffer nur noch "\n" enthält und
Der Eingabepuffer kein EOF enthält wird die Schleife verlassen.

Mal ne grundlegende Frage, weil ich den Sinn des
&& getchar() != EOF
noch nicht verstanden habe:

Ich lerne aus einem Buch und hab jetzt das Kapitel "Entscheidungen treffen" fertig. Das nächste Thema sind Schleifen.

Da ich also noch sehr weit am Anfang stehe, und so ganz direkt noch nichts mit stdin bzw. stdout zutun hatte: Sollte ich jetzt schon verstehen wozu das EOF dient oder wird das später noch behandelt?

Auf die ganze Problematik komme ich deshalb weil, ich nach jedem Kapitel versuche selber ein paar kleine Programme zu schreiben (Bisher meistens Temperaturumrechner o.ä.) und das gelernte Wissen ohne Hilfe anzuwenden, und einige Sachen auszuprobieren. Und da ich halt für mein Hauptmenü meines bescheidenen Temperatur Umrechner auf dieses Problem gestoßen bin, hab ich mal nachgefragt.

Vielen Dank und Viele Grüße
pelzfrucht
Member: rubberman
Solution rubberman Mar 02, 2016 updated at 22:40:28 (UTC)
Goto Top
while (getchar() != '\n'  && getchar() != EOF) { getchar(); }  
Nein, so nicht. Die Schleifen, die ich dir vorgeturnt habe, haben ganz bewusst keinen Schleifenrumpf und erst recht kein zusätzliches getchar(). Jeder Aufruf von getchar() liest ein Zeichen aus dem stdin. Du prüfst ja bei deiner Schleife nicht mal, welcher Wert im Schleifenrumpf von getchar() gelesen wurde.

Sollte ich jetzt schon verstehen wozu das EOF dient
Darin liegt kein großes Geheimnis. EOF ist ein Makro und mit dem Wert -1 definiert. EOF steht für "End Of File".
Nun verstehst du vielleicht auch, warum alle C Funktionen, die ein char einlesen, ein int zurückgeben (und warum ich oben die Variablen als int deklariert habe). Anderenfalls wäre eine Unterscheidung zwischen dem char 0xFF und EOF aka -1 nicht möglich.

hab ich mal nachgefragt
Schadet nie.

BTW Aus welchem Buch lernst du?

Grüße
rubberman
Member: pelzfrucht
pelzfrucht Mar 02, 2016 updated at 22:08:56 (UTC)
Goto Top
Hi @rubberman ,

Nein, so nicht. Die Schleifen, die ich dir vorgeturnt habe, haben ganz bewusst keinen Schleifenrumpf und erst recht kein
zusätzliches getchar(). Jeder Aufruf von getchar() liest ein Zeichen aus dem stdin. Du prüfst ja bei deiner Schleife nicht mal,
welcher Wert im Schleifenrumpf von getchar() gelesen wurde.

while (menue_eingabe != '\n' && getchar() != '\n');   

Mein Fehler. Hab die irgendwie ausversehen etwas ausgeschmückt und die nachfolgenden Fragen alle auf meine falsche while Anweisung gesetzt facepalm

Das ist aber jetzt auf alle Fälle deine und funktioniert auch ohne Probleme.

Darin liegt kein großes Geheimnis. EOF ist ein Makro und mit dem Wert -1 definiert. EOF steht für "End Of File".
Nun verstehst du vielleicht auch, warum alle C Funktionen, die ein char einlesen, ein int zurückgeben (und warum ich oben die Variablen als int deklariert habe). Anderenfalls wäre eine Unterscheidung zwischen dem char
0xFF und EOF aka -1 nicht möglich.

Gut, das hab ich soweit verstanden face-smile

BTW Aus welchem Buch lernst du?
Ist: "C - Programmieren von Anfang an" von Helmut Erlenkötter.
(Ich bin da jetzt auf Seite 63 )

Ich denke dass zu meinem grundlegenden Problem dann erstmal keine Fragen mehr bestehen face-smile Danke!
Eine Frage noch zur while Schleife:

Dort steht ja nur eine Bedingung, der Anweisungsblock jedoch fehlt. Was macht die while Schleife wenn der Anweisungsblock fehlt?

Ich würde es mir so erklären:

Durch den Aufruf
... && getchar() ...

wird ein Zeichen aus stdin ausgelesen, der Vergleich durchgeführt und aus stdin gelöscht.

Und das wiederholt sich solange bis "\n" erreicht wird.

Das würde im Grunde heißen dass der Aufruf von getchar() ein Zeichen ausliest, was auch immer damit gemacht wird und es aus dem stdin löscht.

Korrekt?
Ist zumindestens für mich momentan die einzigste logische Erklärung wie die Schleife vorrankommen kann obwohl sie keinen Anweisungsblock besitzt.

Danke! face-smile
Viele Grüße
pelzfrucht
Member: rubberman
rubberman Mar 02, 2016 at 22:25:21 (UTC)
Goto Top
"C - Programmieren von Anfang an" von Helmut Erlenkötter.
Hmm, das Buch erfreut sich rätselhafterweise großer Beliebtheit. Das Wissen, das dort vermittelt wird ist aber etwa so alt wie ich. Ein main() anstatt int main(void) oder zumindest einer Deklaration mit int als Rückgabetyp ist K&R Style und bereits seit der ersten Standardisierung von C im Jahre 1989 obsolet.

Das würde im Grunde heißen dass der Aufruf von getchar() ein Zeichen ausliest, was auch immer damit gemacht wird und es aus dem stdin löscht.
Richtig. So wie ich es oben schon geschrieben habe, liest jeder Aufruf von getchar() ein Zeichen aus dem stdin. Da es bereits im Schleifenkopf für den Vergleich aufgerufen wird, ist kein Schleifenrumpf/Anweisungsblock nötig. In dem Fall ist es also völlig in Ordnung wenn die Schleife gleich nach dem Kopf mit einem Semikolon abgeschlossen wird. So wird lediglich der Vergleich wiederholt und somit auch der Aufruf von getchar().

Grüße
rubberman
Member: pelzfrucht
pelzfrucht Mar 02, 2016 updated at 22:48:10 (UTC)
Goto Top
Jetzt versteh ich alles. Im Grunde total einfach, keine Ahnung warum ich es mir so umständlich gemacht habe.
Vielen Dank euch allen face-smile (besonders dir @rubberman)

"C - Programmieren von Anfang an" von Helmut Erlenkötter.
Hmm, das Buch erfreut sich rätselhafterweise großer Beliebtheit. Das Wissen, das dort vermittelt wird ist aber etwa so alt wie
ich. Ein main() anstatt int main(void) oder zumindest einer Deklaration mit int als Rückgabetyp ist K&R Style und bereits seit der
ersten Standardisierung von C im Jahre 1989 obsolet.

An sich bin ich eigentlich sehr zufriden mit dem Buch. Da sehr verständlich und einfach (wie ich finde.)
Wahrscheinlich ist es deshalb auch so beliebt.

Würdest du denn von dem Buch abraten?

Würde eigentlich nur ungerne das Buch wechseln. Wenn du allerdings ein Tipp für ein besseres Buch hast, gerne her damit face-smile

Viele Grüße
pelzfrucht
Member: rubberman
rubberman Mar 02, 2016 at 23:01:14 (UTC)
Goto Top
sehr verständlich und einfach
Das wird wohl der Grund sein.

Würdest du denn von dem Buch abraten?
Andere Bücher haben auch ihre Schwachstellen.
Nimm dir aber zumindest die Errata des Autors an die Seite, gewöhne dir an, dass main() vom Typ int ist und mach dich im Nachgang noch einmal mit den Änderungen und Ergänzungen vertraut, die seit K&R C in den Standard gewandert sind.

Grüße
rubberman
Member: Lochkartenstanzer
Lochkartenstanzer Mar 03, 2016 at 07:29:44 (UTC)
Goto Top
Zitat von @rubberman:

while (getchar() != '\n'  && getchar() != EOF) { getchar(); }  

Und jetz überlegen wir uns als Übung, welcher Denkfehler in diesem Konstrukt steckt und warum es trotzdem funktioniert. face-smile

lks

PS: Hint: Eingabe "AB\n" statt "B\n"
Member: rubberman
rubberman Mar 03, 2016 updated at 09:55:15 (UTC)
Goto Top
Hey lks,

das haben wir doch schon geklärt face-wink Jeder Aufruf von getchar() frisst ein Zeichen aus dem stdin oder wartet auf Eingabe. Zusätzlich ist zu berücksichtigen, dass die Prüfung bzw. der Aufruf entsprechend eher abgebrochen wird. Bei der && Verkettung wird das 2. und 3. getchar() nicht aufgerufen, wenn das erste ein '\n' zurückgibt, das 3. getchar() wird nur aufgerufen, wenn die Bedingung aus dem Schleifenkopf erfüllt ist etc pp...
Mist ist das so trotzdem, da (zumindest unter Windows) nach Ctrl+Z noch ein Enter erfolgen muss, damit ein getchar() zurückgibt usw.
Sowas
while (menue_eingabe != '\n' && menue_eingabe != EOF && getchar() != '\n' && !feof(stdin));
fängt den ganzen Kram incl. deiner Einwände bei Dateiumleitung mit unabgeschlossener letzten Zeile noch am besten ab, wobei man die ersten beiden Prüfungen in ein vorgelagertes if Statement auslagern könnte ... Viele Wege führen nach Rom.

Grüße
rubberman
Member: Lochkartenstanzer
Lochkartenstanzer Mar 03, 2016 at 10:41:02 (UTC)
Goto Top
Zitat von @rubberman:

Hey lks,

das haben wir doch schon geklärt face-wink Jeder Aufruf von getchar() frisst ein Zeichen aus dem stdin oder wartet auf Eingabe.

Nee noch nicht nicht ganz. worauf ich hinaus will, ist, daß wenn man dem Ding statt einem zeichen + newline zwei Zeichen plus newline gibt, wartet das Ding "endlos", weil getchar das newline von der getchar-Abrfage auf EOF gefressen wird. face-smile

lks
Member: rubberman
rubberman Mar 03, 2016 at 16:52:46 (UTC)
Goto Top
Ja, hatte ich oben schon mal erklärt face-smile