Probleme mit scanf



  • Hallo Leute,

    mein Problem, der User soll eine numerische Eingabe machen, war diese inkorrekt, muss er die Eingabe wiederholen. Alles kein Problem, außer der User tippt eine nicht nummerischen Wert ein. Habe das Problem zwar mit fflush(stdin) gelöst, habe aber gehört, dass das vermieden werden soll und auf UNIX-artigen OS keinen Sinn hat. Hier mal der Code.

    invalid_input = FALSE;
    do {
      if (invalid_input)
        printf("Bitte w\x84 \bhlen sie einen Wert   zwischen 1 und %i", i - 1);
    
      printf("\n Ihre Eingabe : ");
      if (!scanf("%i", &eingabe))
        fflush(stdin);
    
      invalid_input = TRUE;
    
    } while (!(eingabe >= 1 && eingabe < i));
    

    Ohne das fflush(stdin) wird, wenn der User z.B ein Buchstabe eintippt, hat der User nun garnicht mehr die Möglichkeit was einzutippen. Ich hoffe ihr versteht was ich meine. Wenn ich ein getchar() anstatt des fflush einbaue, geht es zwar, aber eine andere Eingabe mit fgets später im Programm wird dann übersprungen.

    VG



  • Hab nun selbst die Antwort, das Problem ist das scanf allgemein. Es sorgt dafür, dass alle nachfolgenden Eingaben direkt bestätigt werden, da hilft nur nachjedem scanf ein getchar um das Enter abzufangen und in den Müll zu schmeißen.

    Laut POSIX soll man fflush(stdin) nicht benutzen, da es nur für Ausgabestreams konzipiert wurde. Einige sagen man soll fgets und sscanf benutzen. Ich sage viel zu aufwendig. Hier nun meine Lösung:

    do {
    			if (invalid_input)
    				printf(" Ihre Eingabe war inkorrekt. Bitte w\x84 \bhlen sie einen Wert zwischen 1 und %i", i - 1);
    			printf("\n Ihre Eingabe : ");
    
    			scanf("%i", &eingabe);
    			getchar();	// Enter einlesen und wegschmeißen
    
    			invalid_input = TRUE;
    
    		} while (!(eingabe >= 1 && eingabe < i));
    


  • Wenn der Benutzer einen Buchstaben eingibt, aber eine Zahl eingelesen werden soll, gibt scanf einen entsprechenden Wert zurück, den du abfragen kannst.



  • scanf für int hört bei der ersten Nicht-Ziffer auf, d.h. der Rest bleibt in stdin.
    Deshalb ist dein Code Schrott, z.B. bei Eingabe von "123.abc". Da nützt auch dein getchar nichts.

    Kapsele deine Funktionalität in eine Funktion:
    https://www.c-plusplus.net/forum/p2050616#2050616



  • Den eingabepuffer kannst du mit while(fgetc(stdin)!=EOF); leeren.



  • HansKlaus schrieb:

    Den eingabepuffer kannst du mit while(fgetc(stdin)!=EOF); leeren.

    Nein, das geht nicht.

    stdin ist (meist) zeilengepuffert. D.H die eingabe wird erst übernommen, wenn die Entertaste gedrückt wurde.

    Für scanf ist die Entertaste nur ein Whitespace, wie das Leerzeichen.

    Das Problem ist, dass scanf bei, für den Formatspecifier, ungültigen Zeichen abbricht und das Zeichen wieder in den Eingabestrom zurückstellt.

    Das Zurückschreiben ist auch keine Magie, dafür gibt es ungetc/ungetch.
    Man kann also selber alle ungültigen Zeichen überlesen und das erste gültige Zeichen wieder zurückschreiben.

    Bis zum Zeilenende lesen ist eine Möglichkeit.



  • Danke für die hilfreichen Antworten. Eine zusätzliche Funktion ist zwar gut, aber ist für mich zuviel Overhead.

    Hier nun eine Lösung, was haltet ihr davon ?

    do {
    	if (invalid_input)
    		printf(" Ihre Eingabe war inkorrekt. Bitte w\x84 \bhlen sie einen Wert zwischen 1 und %i", avail_words - 1);
    
    	printf("\n Ihre Eingabe : ");
    
    	scanf("%i", &eingabe);
    	while (fgetc(stdin) != 10);	// Enter und restlichen Müll einlesen und wegschmeißen
    
    	invalid_input = TRUE;
    
    } while (!(eingabe >= 1 && eingabe < avail_words));
    


  • Das ist besser. Macht aber bei Eingaben wie 123.abc immer noch das, was du wohl willst. Versuchs so:

    #include <stdio.h>
    #include <limits.h>
    
    #define TRUE (1)
    #define FALSE (0)
    
    int main (void)
    {
        int invalid_input = FALSE;
        int eingabe;
        int avail_words = INT_MAX;
        do {
            if (invalid_input)
                printf(" Ihre Eingabe war inkorrekt. Bitte w\x84 \bhlen sie einen Wert zwischen 1 und %i", avail_words - 1);
    
            printf("\n Ihre Eingabe : ");
    
            if (scanf("%i", &eingabe) != 1)
                eingabe = avail_words;  /* das ist ein wert, der die schleife nicht
                                           beendet */
    
            while (fgetc(stdin) != 10); // Enter und restlichen Müll einlesen und wegschmeißen
    
            invalid_input = TRUE;
    
        } while (!(eingabe >= 1 && eingabe < avail_words));
        printf("Eingabe: %d\n", eingabe);
        return 0;
    }
    

    Ich hab nur geändert, dass das Ergebnis von scanf() genau 1 sein muss. So sollte es klappen.



  • und keine Magic Numbers wie 10. Da schreibt man '\n' und jeder weiß was gemeint ist.
    (auch bei anderen Zeichensätzen)



  • nur daß '\n' je nach Plattform 10, 13 oder 13 10 sein kann.



  • versionsnummer schrieb:

    nur daß '\n' je nach Plattform 10, 13 oder 13 10 sein kann.

    So mag es in einer Datei aussehen.
    Bei Ein-/Ausgabe, die nicht im binary-Mode arbeitet, wird das durch '\n' beschrieben.



  • echo -e "#include \"stdio.h\"\nint main(){ printf(\"hallo\\\\n\");}" | wine gcc -o t_e_s_t.exe -xc - && wine ./t_e_s_t.exe | hexdump -C
    

    68 61 6c 6c 6f 0d 0a

    echo -e "#include \"stdio.h\"\nint main(){ printf(\"hallo\\\\n\");}" | gcc -o t_e_s_t -xc - && ./t_e_s_t | hexdump -C
    

    68 61 6c 6c 6f 0a



  • Imlerith schrieb:

    Eine zusätzliche Funktion ist zwar gut, aber ist für mich zuviel Overhead.

    Depp.



  • manchmal reicht in der tat ein "\n" und manchmal muss es "\r\n" sein. aber "newline" ist doch genormt, oder irre ich da?



  • versionsnummer schrieb:

    echo -e "#include \"stdio.h\"\nint main(){ printf(\"hallo\\\\n\");}" | wine gcc -o t_e_s_t.exe -xc - && wine ./t_e_s_t.exe | hexdump -C
    

    68 61 6c 6c 6f 0d 0a

    echo -e "#include \"stdio.h\"\nint main(){ printf(\"hallo\\\\n\");}" | gcc -o t_e_s_t -xc - && ./t_e_s_t | hexdump -C
    

    68 61 6c 6c 6f 0a

    Unsinn.
    Du weißt nicht, was für eine Unwissenheit du hier verbreitest.
    Wie immer heißt es hier: Klappe halten, wenn man keine Ahnung hat.

    DirkB hat völlig recht, == 10 ist immer falsch, == '\n' ist immer richtig, da die Lineendconvention implementierungsabhängig (hier auch plattformabhängig) ist; bei zeichenorientierten Streams (stdin,stderr,stdout+fopen(..."r") kapselt der Compiler die Binärausprägung der konkreten Lineendconvention automatisch bei der Verwendung von '\n', im Gegensatz zu dir immer und in jedem Fall richtig.



  • LOL



  • verionsnummer schrieb:

    LOL

    Genau. Lache nur ruhig über dich selber, halte die Klappe und verziehe dich dorthin wo du herkamst.



  • Der Test von versionsnummer bestätigt doch nur das Geschriebene bezüglich \n

    Im Code steht \n. In der Ausgabe steht die Zeilenendekennung vom Betriebssystem.


Anmelden zum Antworten