Scanf und Buchstaben ;)



  • richard1092 schrieb:

    Nachdem beide Zahlen eingegeben wurden kommt das "Sie haben einen Fehler gemacht. Erneut versuchen J/N".
    Und das Programm beednet sich genau in diesem Moment.
    Bei richtiger Eingabe passt alles.

    Auch bei korrekter Eingabe der beiden int s bleibt ein '\n' im Puffer, welches von deinem

    scanf ("%c", &choice);
    

    konsumiert wird ( choice == 10 ).

    Danach kommt immer

    if (choice == 'j' || choice =='J') // reading out choice, restart or program end
    {
    	x=1;
    
    } else {
    
    	x--; // <- das. btw: warum nicht einfach x = 0 ?
    }
    

    Die Lösung ist wie hier schon erklärt, den Eingabepuffer zu leeren.

    ps&btw: "jes/no" ?



  • Du kannst auch mal ein Leerzeichen in dem Formatsring von scanf vor dem % probieren:

    scanf (" %c", &choice);
            ^ da
    

    C99 7.19.6.2 The fscanf function schrieb:

    5. A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read.
    ...
    8. Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier.



  • Swordfish schrieb:

    Hm, eine Schleife, zwei if und eine Variable vs. eine Schleife?
    Ich nehm' die Schleife 🤡

    Du spottest über eine vermeintliche Verschlechterung. Siehst du nicht?
    Ich habe eine Zeile im Hauptprogramm, die dasselbe bewirkt wie 12 von dir.
    Des Weiteren ist es guter Stil die Ein- und Ausgabefunktionen auszulagern, u.a. um Spaghetticode zu vermeiden.
    Dafür spendiere ich doch gern ein if und lege noch eine Variable oben druff!
    Hugh! 🤡

    Zugegeben die Funktion integer_input ist noch ein wenig verbesserungswürdig, weil sie dem Hauptprogramm eine Eingabe aufzwingt
    - das Hauptprogramm hat keine Chance bei einer falschen Eingabe abzubrechen/zu verzweigen.
    Darum habe ich meinen Senf ein wenig überarbeitet, guck:

    int integer_input ( int* number )
    {
    	int ret = scanf ( "%d", number );
        clear_stdin();
    	if ( 1 != ret )
    		puts ( "Input error!" );
    	return 1 != ret;
    }
    
    int main(void)
    {
        int a = 0, b = 0, choice = 0;
        do
        {
            choice = 0; /// !!!!
    
    		do
    			printf("Enter an even number: ");
    		while ( integer_input ( &a ));
    
    		do
    			printf("Enter an odd number: ");
    		while ( integer_input ( &b ));
    
            if ( !( a % 2 ) && ( b % 2 ))
                choice = 'n', puts( "Well done!");
            else
                puts( "At least one input didn't meet requirements!" );
    
            while ( choice != 'y' && choice != 'n' )
                puts( "Do you want to try again? (y/n)" ), choice = getchar(), clear_stdin();
    
        } while ( choice == 'y' );
    
        puts("Hit enter to quit.");
        getchar();
        return 0;
    }
    

    Was sagst du jetzt? 🕶
    Btw. hast du deinen Logikfehler gefunden?



  • DirkB schrieb:

    Du kannst auch mal ein Leerzeichen in dem Formatsring von scanf vor dem % probieren

    Danke!

    C Ästhetiker schrieb:

    Swordfish schrieb:

    Hm, eine Schleife, zwei if und eine Variable vs. eine Schleife?
    Ich nehm' die Schleife 🤡

    Du spottest über eine vermeintliche Verschlechterung. Siehst du nicht?

    Schonschon. Aber bis zu diesem Punkt hast du nur gegen die Verwendung des Kommaoperators argumentiert. Er ist so angewandt wie er gedacht ist, so what's the point?

    C Ästhetiker schrieb:

    Ich habe eine Zeile im Hauptprogramm, die dasselbe bewirkt wie 12 von dir.
    Des Weiteren ist es guter Stil die Ein- und Ausgabefunktionen auszulagern, u.a. um Spaghetticode zu vermeiden.

    Ich habe nie gesagt, daß ich gegen das auslagern redundanten Codes in Funktionen gesagt. 💡

    C Ästhetiker schrieb:

    int integer_input ( int* number )
    {
    	int ret = scanf ( "%d", number );
    	clear_stdin();
    	if ( 1 != ret )				/* Dann überlass auch die */
    		puts ( "Input error!" );	/* Fehlerbehandlung dem caller ... */
    	return 1 != ret;	/* im Erfolgsfall false zurückgeben? */
    }
    
    #include <ctype.h>
    #include <string.h>
    #include <stdio.h>
    
    void clear_stdin( void )
    {
        int ch = 0;
        while( ( ( ch = getchar() ) != '\n' ) && ( ch != EOF ) );
    }
    
    int request_int( int *dst, char const *prompt = 0, int may_fail = 0 )
    {
        int success = 0;
    
    	while( prompt && printf( "%s: ", prompt ),
    			( success = scanf ( "%d", dst ) ) != 1 && !may_fail ) {
    
            clear_stdin();
            printf( "Input error! Please try again.\n\n" );
        }
    
    	return success;
    }
    
    int request_char( char *dst, char const* valid_chars, char const *prompt = 0, int may_fail = 0 )
    {
        int success = 0;
    
    	while( prompt && printf( "%s ", prompt ),
    			!( ( success = scanf ( " %c", dst ) ) == 1 && ( !valid_chars || strchr( valid_chars, *dst ) ) ) || may_fail ) {
    
            clear_stdin();
            printf( "Input error! Please try again.\n\n" );
        }
    
    	return success;
    }
    
    int main()
    {
        int a = 0; // declaration and definition of needed variables
        int b = 0;
        char choice = '\0';
    
        do {
            request_int( &a, "Enter an even number" );
    		request_int( &b, "Enter an odd number" );
    
            if( !( a % 2 ) && ( b % 2 ) ) {
    
                printf( "\nYou are able to tell the difference between odd and even numbers. Congratulations!\n");
                choice = 'n';
    
            } else {
    
                printf( "\nAt least one input didn't meet requirements!\n\n" );
    			request_char( &choice, "yYnN", "Do you want to try again? (y/n)" );
                printf( "\n\n" );
            }
    
        } while( tolower( choice ) == 'y' );
    }
    

    C Ästhetiker schrieb:

    Btw. hast du deinen Logikfehler gefunden?

    Ja. Deine Zuweisung an choice am Beginn des Schleifenkörpers ist aber trotzdem unnötig.



  • Swordfish schrieb:

    Schonschon. Aber bis zu diesem Punkt hast du nur gegen die Verwendung des Kommaoperators argumentiert.
    Er ist so angewandt wie er gedacht ist, so what's the point?

    Eher gegen das printf in der while Schleife, weil es keine Funktion hat außer den Code schwerer lesbar zu machen,
    aber recht hast du, die Kommaoperatoren fand ich dort zusammen mit dem printf auch verwirrend
    (du ja vielleicht auch, wegen des misslungenen Schleifenkopfs? (der musste ja jetzt kommen, ne ;))).

    Swordfish schrieb:

    Ich habe nie gesagt, daß ich gegen das auslagern redundanten Codes in Funktionen gesagt. 💡

    Full ACK. (Du hattest es lediglich praktiziert. *breitgrins und wechduck*)

    Swordfish schrieb:

    return 1 != ret; /* im Erfolgsfall false zurückgeben? */

    Neineineineinein, return 1 == Funktion liefert einen Fehler,
    return 0 == Funktion liefert Null Fehler.

    Swordfish schrieb:

    /* Dann überlass auch die /
    puts ( "Input error!" ); /
    Fehlerbehandlung dem caller ... */

    Das sehe und mache ich prinzipiell auch so. War wohl noch nicht richtig wach. (Warum machst du das nicht?)

    Swordfish schrieb:

    int request_char( char *dst, char const* valid_chars, char const *prompt = 0, int may_fail = 0 )
    { 
    ...
     clear_stdin();
        printf( "Input error! Please try again.\n\n" ); // Wasser predigen Wein trinken  :D  (not caller-driven)  
    ...
    

    ->

    int int_input ( int* number, int report )
    {
        int ret = scanf ( "%d", number );
        clear_stdin();
        if ( ret )
            return 0;
       else if ( report ) 
           return emsg ( "Input error!" );   // caller driven message output
       else
           return 1; // dito(kein output)
    }
    // Da die Funktion int_input nicht zur Eingabe einer gültigen Zahl zwingt, 
    // kann im Programm unterschiedlich auf die Antwort reagiert werden:
    do
        printf ( "Enter an odd number:" ); 
    while ( int_input ( &b, 1 ));  // Eine gültige Eingabe erzwingen.
    
    if ( int_input ( &b, 1 ) )
        return msg ( "Danger of CPU overheating! Go somewhere else and learn how to type numbers, then come back and try again.");  // Funktion oder Programm verlassen.
    

    Swordfish schrieb:

    Deine Zuweisung an choice am Beginn des Schleifenkörpers ist aber trotzdem unnötig.

    Is woll nötig, eildieweil man bei fehlerhafter Eingabe ansonsten gar nicht mehr "Do you want to try again? (y/n)" gefragt würde.

    Swordfish schrieb:

    int request_char( char *dst, char const* valid_chars, char const *prompt = 0, int may_fail = 0 )
    

    Defaultparameter? Das muffelt verdächtig nach C++, oder ist das mittlerweile C99 Standard? Dem C99 hinke ich meilenweit hinterher ächts.

    Swordfish schrieb:

    ...
     while( prompt && printf( "%s ", prompt ),
                !( ( success = scanf ( " %c", dst ) ) == 1 && ( !valid_chars || strchr( valid_chars, *dst ) ) ) || may_fail ) 
    ...
    

    😮
    Ein wenig kryptisch das geworden ist und noch schwerer zu verstehen, da bräuchte ich ja schon Zettel und Stift bei
    den vielen Möglichkeiten, um das zu kapieren! Das geht ja gaaar nicht ... ich bin eher fürs schlichte, leicht verständliche, ästhetische.
    Oder glaubst du ich hatte mir nur so diesen edlen Namen zugelegt? Ich bin doch nicht zum Spaß hier! 🤡
    Dafür ist deine main jetzt schön aufgeräumt. 👍

    Was wird das eigentlich, online team homework development?
    Ich habe deine validate Idee geklaut und den Inhalt der main ein wenig nach meiner Nase abgeändert, natürlich mit redundancy-free while conditions :p .

    // Ausgelagerte Ausgabe(n) -> wartungs && multi language development friendly.
    int msg ( const char* s )
    {
    	printf ( "%s", s );
    	return 1;
    }
    
    int validate_char ( const char* valid_set, int c )
    {
    	if ( EOF == c )
    		return 1; // ERROR
    	return NULL == strchr ( valid_set, c ); // 1: ERROR, 0: OK
    }
    
    void clear_stdin ( void )
    {
    	int c;
    	while (( c = getchar()) != '\n' && c != EOF )
    	{}
    }
    
    int int_input ( int* number, int verbose )
    {
        int ret = scanf ( "%d", number );
        clear_stdin();
        if ( ret )
            return 0;
       else if ( verbose ) 
           return msg ( "Input error!\n" );   // caller driven message output
       else
           return 1; // dito
    }
    
    int char_input ( int* c, const char* valid_set, int verbose )
    {
    	if (( *c = getchar()) != '\n' )
    		clear_stdin();
    
    	if ( validate_char ( valid_set, *c ))
    	{
    		if ( verbose ) 
    			return msg ( "Input error! " );   // caller driven message output
    		else
    			return 1; // dito
    	} // auto else implicit
    	return 0;
    }
    
    int main ( void )
    {
        int a = 0, b = 0, choice = 0;
        do 
    	{
    		do
    			msg ( "Enter an even number: ");
            while ( int_input ( &a, 1 ));
    		do
    			msg ( "Enter an odd number: ");
    		while ( int_input ( &b, 1 ));
    
    		if ( !( a % 2 ) && ( b % 2 ))
    			msg ( "Well done!\n" );
    		else
    			msg ( "At least one input doesn't meet requirements!\n" );
    		do
    			msg ( "Do you want to try again(y/n)? " );
    		while ( char_input ( &choice, "YyNn", 1 ));
    
    	} while ( choice == 'y' || choice == 'Y' );
    
        return 0;
    }
    


  • Swordfish schrieb:

    Wenn links false ist wird rechts nicht ausgewertet... deswegen auch die Klammerei.

    edit: Short-circuiting §§6.3.13 u. 6.3.14

    Du meine Güte.
    Du hast bei deinem ganzen Klammerungskram den Überblick zw. true und false verloren. Rechts wird choice ausgewertet, wenn links true ergibt. Links ergibt true, wenn scanf !=1 liefert also fehlerhaft interpretiert wurde, was zu einem fraglichen Inhalt von choice führt, den du rechts dann auswertest.



  • Big Brother the genuine schrieb:

    Neineineineinein, return 1 == Funktion liefert einen Fehler,
    return 0 == Funktion liefert Null Fehler.

    Ziemlich gewöhnungsbedürftig für C, um nicht ein schlimmeres Wort zu gebrauchen.

    Big Brother the genuine schrieb:

    int int_input ( int* number, int report )
    {
        int ret = scanf ( "%d", number );
        clear_stdin();
        if ( ret )
            return 0;
    

    Falsch.
    Liefert scanf EOF, liefert deine Funktion true in deinem Sinne, was natürlich unsinnig ist.



  • Wutz schrieb:

    Big Brother the genuine schrieb:

    int int_input ( int* number, int report )
    {
        int ret = scanf ( "%d", number );
        clear_stdin();
        if ( ret )
            return 0;
    

    Falsch.
    Liefert scanf EOF, liefert deine Funktion true in deinem Sinne, was natürlich unsinnig ist.

    Fu** das hab ich vergessen zu ändern. 😡
    Da haddu natürlich Recht.

    Wutz schrieb:

    Big Brother the genuine schrieb:

    Neineineineinein, return 1 == Funktion liefert einen Fehler,
    return 0 == Funktion liefert Null Fehler.

    Ziemlich gewöhnungsbedürftig für C, um nicht ein schlimmeres Wort zu gebrauchen.

    Dafür, das es gewöhnungsbedürftig ist, macht es die C-Standardbibliothek relativ häufig, eine 0 nach erfolgreicher Ausführung einer Funktion zu liefern. Um es mal mild zu formulieren.



  • Ach ja?
    Welche Bibliothekfunktionen mit Boolean-Rückgabeverhalten liefern denn 0 für true und !=0 für false?



  • Wutz schrieb:

    Ach ja?
    Welche Bibliothekfunktionen mit Boolean-Rückgabeverhalten liefern denn 0 für true und !=0 für false?

    Eigentlich ging es ja um:

    Wutz schrieb:

    Big Brother the genuine schrieb:

    Neineineineinein, return 1 == Funktion liefert einen Fehler,
    return 0 == Funktion liefert Null Fehler.

    Ziemlich gewöhnungsbedürftig für C, um nicht ein schlimmeres Wort zu gebrauchen.

    Das ist ja nicht unbedingt dasselbe.

    Rückgabewerte für 0 für erfolgreich und !0 für Fehler liefern u.a
    remove und rename, atexit,



  • Wutz schrieb:

    Ach ja?

    Jepp!

    Wutz schrieb:

    Welche Bibliothekfunktionen mit Boolean-Rückgabeverhalten liefern denn 0 für true und !=0 für false?

    Wo siehst du da nen boolean? Ich sehe dort einen int.
    Wollte ich einen boolschen Rückgabetyp, stünde dort bool oder ähnliches.

    DirkB schrieb:

    ...
    Rückgabewerte für 0 für erfolgreich und !0 für Fehler liefern u.a
    remove und rename, atexit,

    Und ein ganzer Sack voll mehr.


Anmelden zum Antworten