Kleines Programm: Schwierigkeiten beim Abfangen von Fehleingaben



  • Hallo!

    Zur Übung schreibe ich ein kleines Programm, welches mir berechnen soll, welchen Wochentag man zum Beispiel nach 64 Tagen haben wird, wenn heute Sonntag ist. Dabei wird jedem Wochentag eine Integer Zahl zugeordnet (Montag = 1, Dienstag = 2 etc.)

    Zur Auswahl des heutigen Tages gibt man also einen Integewert an.
    Dabei sollen alle möglichen Fehleingaben, also alles was keine ganze Zahl zwischen 1 bis 7 ist, abgefangen werden.

    Auszug aus dem Programm:

    #include <stdio.h>
    
    int main(void)
    {
        int tag, anzahl;
        char buffer[100];
    
        /*Bildschirmausgabe: Begrüßung und Optionen*/
        printf("Hallo!\n");
        printf("Welcher Tag ist heute?\n");                    
        printf("[1] Montag\n");
        printf("[2] Dienstag\n");
        printf("[3] Mittwoch\n");
        printf("[4] Donnerstag\n");
        printf("[5] Freitag\n");
        printf("[6] Samstag\n");
        printf("[7] Sonntag\n");
    
        /*Auswahl des Tages durch Eingabe eines Integers, Fehleingaben abfangen*/                
        do {                        
               fgets(buffer,100,stdin);
               sscanf(buffer,"%i",&tag);
    
               if ( tag<1 || tag>7 ) printf("Fehleingabe!\n");
    
           } while ( ! (tag>0 && tag<8) );
    

    Ein Problem dabei:

    Wenn ich Beispielsweise keinen integer Wert eingebe, sondern einen Buchstaben, dann nimmt die Variable tag unabhängig vom eingebenen Buchstaben den Wert 2 an.

    Wie kommt dieser Wert zustande und warum ausgerechnet die Zahl 2?

    Ich hätte vielleicht eine 0 erwaret oder den ASCII Wert des Buchstaben, aber eine 2?..

    Bin echt ratlos.. Hat jemand vielleicht einen Hinweis für mich?

    Gruß ser_u



  • Bei mir ist es "-858993460", typischer Wert für eine uninitialisierte Variable.
    Wenn sscanf fehlschlägt füllt es die Variable auch nicht, deshalb steht da nur Müll drin. Deshalb den Rückgabewert von sscanf auswerten. 😉



  • DarkShadow44 schrieb:

    Bei mir ist es "-858993460", typischer Wert für eine uninitialisierte Variable.
    Wenn sscanf fehlschlägt füllt es die Variable auch nicht, deshalb steht da nur Müll drin. Deshalb den Rückgabewert von sscanf auswerten. 😉

    Danke für den Tip.

    Hab den Programmabschnitt mal entsprechend geändert und eine Testvariable eingefügt. Es funktioniert jetzt fast einwandfrei:

    int main(void)
    {
        int tag, anzahl, test;
        char buffer[100];
    
        /*Bildschirmausgabe: Begrüßung und Optionen*/
    
        .
        .
        .
    
        /*Auswahl des Tages durch Eingabe eines Integers, Fehleingaben abfangen*/                
        do {                        
               fgets(buffer,100,stdin);
               test = sscanf(buffer,"%i",&tag);
    
               if ( tag<1 || tag>7 || test == 0) printf("Fehleingabe!\n");
    
           } while ( ! (tag>0 && tag<8 && test == 1) );
    

    Der Wert der Variablen test zeigt an, wie viele Integerwerte erfolgreich eingelesen wurden (stimmt die Aussage?).

    Habe aber noch das Problem, dass Zeichenketten, die mit einer Zahl beginnen, nicht abgefangen werden:

    Zum Beispiel: 3awdwa oder 1.4 oder 1,5 usw.

    Lässt sich da was machen oder ist es mit fgets/sscanf nicht möglich, solche Fehleingaben abzufangen.



  • Du kannst nachsehen, ob noch mehr Zeichen drin sind.

    char c;
    ....
    test = sscanf(buffer,"%i %c",&tag,&c);
    

    Wenn test != 1 ist, war was faul.
    Das Leerzeichen vor dem %c überliest Whitespace im Text.

    %i ist hier nicht so problematisch, da du nur Ziffern bis 7 haben willst.
    Aber es erkennt das Zahlensystem am Prefix der Zahl. Also sind auch Hexadezimalzahlen (0x3) oder Oktalzahlen (07) möglich.
    08 wäre da schon nicht möglich.



  • Ich würde das jetzt auf die Schnelle so lösen:

    int tag, anzahl, test; 
        char buffer[100]; 
    
    	/*Bildschirmausgabe: Begrüßung und Optionen*/ 
    
    	/*Auswahl des Tages durch Eingabe eines Integers, Fehleingaben abfangen*/                 
    	do {    
    
    		fgets(buffer,100,stdin); 
    		char*endptr;
    		tag = strtol(buffer, &endptr, 10);
    		bool success = (buffer != endptr && endptr[0] == '\0') && (tag > 0 && tag < 8);
    
    		if (!success) printf("Fehleingabe!\n"); 
    
    	} while (!success);
    

    endptr gibt dei Addresse des Puppers nach dem Einleseversuch an. Wenn sie gleich der Startadresse ist waren es falsche Daten. Wenn die Adresse verschoben ist, aber das nächste Zeichen kein '\0' ist, kommen nach der Zahl noch Daten und wir melden ein Fehleingabe.

    EDIT: Ich denke mal wieder viel zu kompliziert...



  • Die Variante von DirkB gefällt mir doch sehr gut. Sehr intuitiv.

    Hab mal aus dem %i ein %d gemacht, weil ich wirklich nur folgende Möglichkeiten als Eingabe durchlassen möchte:

    1, 2, 3, 4, 5, 6, oder 7 und nichts anderes, auch wenn es vom Wert her äquivalent sein sollte ( zBsp. 0x3 = 3 )

    So sieht der Abschnitt jetzt aus:

    int main(void)
    {
        int tag, anzahl, test;
        char buffer[100], c;
    
        /*Bildschirmausgabe: Begrüßung und Optionen*/
        .
        .
        .
    
        /*Auswahl des Tages durch Eingabe eines Integers, Fehleingaben abfangen*/                
        do {                        
               fgets(buffer,100,stdin);
               test = sscanf(buffer,"%d %c",&tag, &c);
    
               if ( tag<1 || tag>7 || test != 1) printf("Fehleingabe!\n");
    
           } while ( ! (tag>0 && tag<8 && test == 1));
    

    Jetzt habe ich Dank euren Tipps schon fast alle Fehleingaben abgefangen, nur eine Sache kommt noch durch (jedenfalls hab ich noch keine weitere bemerkt): Nämlich Dezimalzahlen von 1-7 die eine beliebige Anzahl von Nullen vor sich haben:

    Beispiel: 002, 000000003, 01 etc.

    Komm ich da mit fgets/sscanf da an die Grenzen des Möglichen oder gibt es auch hier einen Trick?

    Mir fällt da auf Anhieb nichts ein...

    Gibt es vielleicht eine Möglichkeit zu erfahren, wie viele Stellen die eingelesene Zahl hat, ohne die Nullen zu ignorieren?



  • 002, 000000003, 01 sind gültige Zahlen. Warum willst du die abfangen?

    Aber sei's drum. Du möchtest demnach nur eine Stelle einlesen.
    Dann sag das doch dem sscanf

    test = sscanf(buffer,"%1d %c",&tag, &c);
    

    Du kannst aber auch die Länge der bisherigen Eingabe ermitteln. Dafür gibt es %n

    int anz; // für die Anzahl bisher gelesener Zeichen
    ...
    test = sscanf(buffer,"%d%n %c",&tag, &anz, &c);
    


  • DirkB schrieb:

    002, 000000003, 01 sind gültige Zahlen. Warum willst du die abfangen?

    Natürlich hast du Recht. Es geht mir auch nur ums Lernen gerade, das Programm dient wie gesagt nur der Übung.

    Werd mir demnächst das Buch von Kernighan und Ritchie besorgen, dann werd ich nicht mehr jede Kleinigkeit im Forum nachfragen müssen.

    Aber eine kleine Frage würde ich gerne noch stellen zu dem Thema:

    Gibt es in C die Möglichkeit, Eingaben zu tätigen ohne mit Return zu bestätigen?

    Zum Beispiel tippe ich nur 2 auf meiner Tastatur und die 2 wird dem Programm übergeben, ohne dass ich nochmal Enter drücken muss.



  • Das hatten wir in C++ Ich habe probleme mit char bitte helfen vor kurzem.
    Portabel siehts da schlecht aus, aber so zum testen nimm einfach mal "getch()" aus "conio.h". Info dazu: Die Taste wird nur eingelesen und steht dann nicht in der Konsole, wenn du willst dass der Benutzer seinen Input auch sieht musst du dich manuell darum kümmern (oder "getche()" nutzen).
    Aber AFAIK ist das nichts was im Standard wäre, hindert dich aber ja nicht dran es zu Lernzwecken zu nutzen. 😉



  • Bei Windows gibt es getch() und getche() (mit e für Echo). Nicht portabel.

    Das sind dann Probleme für die WinAPI.
    Aber lies selber: http://www.c-plusplus.net/forum/39320-full.

    Unter Linux kannst du Curses (oder Ncurses) nehmen.

    Wenn du wissen willst welche Funktionen die C-Standardbibliothek bietet, dann schau mal bei http://www.cplusplus.com/reference/clibrary/ oder http://de.cppreference.com/w/ nach.



  • Danke für eure Beiträge!



  • Hab mal diese Stelle nochmal mit getch() versucht und ein, finde ich, für diese Situation verbessertes Ergebnis erzielt:

    #include <stdio.h>
    #include <conio.h>
    
    int main(void)
    {
        int tag ...    
    
        /*Bildschirmausgabe: Begrüßung und Optionen*/
        printf("\nHallo!\n\n");
        printf("Welcher Tag ist heute?\n\n");                    
        printf("[1] Montag\n");
        printf("[2] Dienstag\n");
        printf("[3] Mittwoch\n");
        printf("[4] Donnerstag\n");
        printf("[5] Freitag\n");
        printf("[6] Samstag\n");
        printf("[7] Sonntag\n");
    
        /*Auswahl des Tages durch Eingabe einer Ziffer von 1 bis 7, Fehleingaben abfangen*/                
        do{     
            tag = getch() - 48;
    
            if ( tag < 1 || tag > 7 )
               printf("\nFehleingabe!\n");
    
        } while ( ! ( tag > 0 && tag < 8) );
    
        .
        .
        .
    

    Hab ich das richtig verstanden, dass der Rückgabewert der Funktion getch() der ASCII-Code des eingelesenen characters ist? (Daher die - 48)

    Und ist es üblich, getch() so anzuwenden, wie ich es gemacht habe?

    Jedenfalls lassen sich Fehleingaben damit deutlich einfacher abfangen.


Anmelden zum Antworten