Forum: PC-Programmierung scanf in C drei Zeichen einlesen


von Leyla S. (keeke)


Lesenswert?

Hallo,

diesmal geht's darum, drei Zeichen mit scanf einzulesen. Ich habe zwei 
Versionen, wobei Version 1 nicht geklappt hat. Dann habe ich weiter 
ausprobiert und kam auf die zweite Version, in dem ich alle Zeichen auf 
einmal eingelesen habe. Wo liegt der Fehler bei der ersten Version?

Mein erster Versuch:
1
#include <stdio.h>
2
int main(void)
3
 {
4
    char x,y,z;
5
    printf("Bitten geben Sie das 1.Zeichen ein: \n");
6
    scanf("%c",&x);
7
    printf("Bitten geben Sie das 2.Zeichen ein: \n");
8
    scanf("%c",&y);
9
    printf("Bitten geben Sie das 3.Zeichen ein: \n");
10
    scanf("%c",&z);
11
    return 0;
12
}
Mein zweiter Versuch:
1
#include <stdio.h>
2
int main(void)
3
 {
4
    char x,y,z;
5
    printf("Bitte 3 Zeichen eingeben: \n");
6
    scanf("%c %c %c", &x,&y,&z);
7
    return 0;
8
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Der Fehler bei deinem ersten Versuch liegt darin, dass du ja nicht 3 
Zeichen eingibst, sondern deren 6.
Du tippst natürlich die 3 Zeichen, die du eingeben willst. Aber: Du 
tippst dazwischen bzw. nach dem letzten ja auch jedesmal auf 'Return'. 
Und auch dieses jeweilige Return ist ein Zeichen, das du bzw. das 
Programm behandeln musst. Es wird dir ganz normal als eine gültige 
Eingabe für %c geliefert.

: Bearbeitet durch User
von Rainer V. (rudi994)


Lesenswert?

nach jedem scanf() mit fflush(stdin) den internen Eingabepuffer leeren.

von Leyla S. (keeke)


Lesenswert?

Achso Dankeschön :)
Nur eine Sache, die ich nicht ganz verstehe, warum kommt es bei Nutzung 
von int und Eingabe von ganzen Zahlen nicht zu dieser Pufferung?
1
int main(void)
2
 {
3
    int x,y,z;
4
    printf("Bitten geben Sie das 1.Zeichen ein: \n");
5
    scanf("%d",&x);
6
    printf("Bitten geben Sie das 2.Zeichen ein: \n");
7
    scanf("%d",&y);
8
    printf("Bitten geben Sie das 3.Zeichen ein: \n");
9
    scanf("%d",&z);
10
    return 0;
11
}

von Leyla S. (keeke)


Lesenswert?

Okey danke für den Hinweise mit fflush(stdin). Das erste klappt jetzt:)

von Karl H. (kbuchegg)


Lesenswert?

Leyla S. schrieb:
> Achso Dankeschön :)
> Nur eine Sache, die ich nicht ganz verstehe, warum kommt es bei Nutzung
> von int und Eingabe von ganzen Zahlen nicht zu dieser Pufferung?

Weil ein %d sich automatisch soweit vortastet, bis es zu einer Zahl 
kommen kann. D.h das %d überliest selbsttätig den Return und eventuelle 
andere Leerzeichen (sog. Whitespace-Zeichen).
Ein %c tut das aber nicht. Der liefert dir genau das nächste Zeichen, 
welches im Eingabestrom daher kommt.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Leyla S. schrieb:
> Okey danke für den Hinweise mit fflush(stdin). Das erste klappt jetzt:)

Ist aber genau genommen nicht garantiert, auch wenn viele 
Implementierungen das so handhaben.
fflush ist eigentlich nur für Ausgabe-Streams definiert. Was passiert, 
wenn man fflush auf einen Eingabe-Stream anwendet ist 
implementierungs-abhängig.

von Leyla S. (keeke)


Lesenswert?

Nochmal danke :)

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl Heinz schrieb:
> Leyla S. schrieb:
>> Okey danke für den Hinweise mit fflush(stdin). Das erste klappt jetzt:)
>
> Ist aber genau genommen nicht garantiert, auch wenn viele
> Implementierungen das so handhaben.
> fflush ist eigentlich nur für Ausgabe-Streams definiert. Was passiert,
> wenn man fflush auf einen Eingabe-Stream anwendet ist
> implementierungs-abhängig.

Schlimmer noch: Es ist sogar undefined Behavior. fflush sollte deswegen
überhaupt nicht auf Eingabeströme angewandt werden.

Hier der entsprechende Auszug aus dem Standard:
1
Description
2
3
If stream points to an output stream or an update stream in which the
4
most recent operation was not input, the fflush function causes any
5
unwritten data for that stream to be delivered to the host environment
6
to be written to the file; otherwise, the behavior is undefined.
7
8
If stream is a null pointer, the fflush function performs this flushing
9
action on all streams for which the behavior is defined above.

Auf meinem Linux-Rechner hier passiert bei fflush(stdin) einfach gar
nichts, aber selbst ein Systemabsturz bei diesem Aufruf wäre
standardkonform.

Hier sind noch zwei FAQs (mit Antworten ;-)) zu dem Thema:

  http://c-faq.com/stdio/stdinflush.html
  http://c-faq.com/stdio/stdinflush2.html

Im konkreten Fall kann man das CR-Zeichen und ggf. weiteren Whitespace
dadurch überlesen, dass man in den Formatstrings jeweils vor dem %c noch
ein Leerzeichen einfügt:
1
    scanf(" %c",&x);

Damit kann man dann allerdings auch keine Leerzeichen mehr einlesen. Da
scanf alle Whitespace-Zeichen (Leerzeichen, Tab, CR, LF usw.) gleich
behandelt, ist es zum Einlesen von solchen Zeichen ziemlich ungeeignet.
Man nimmt dann lieber getchar (zum Einlesen einzelner Zeichen) oder
fgetc (zum Einlesen ganzer Zeilen).

von Rolf Magnus (Gast)


Lesenswert?

Rainer V. schrieb:
> fflush(stdin)

Das wird zwar manchmal gerne gemacht, ergibt aber eigentlich überhaupt 
keinen Sinn und ist in C auch nicht definiert.

von Michael L. (michalabitzke)


Lesenswert?

Ich würde das im Puffer stehende carrige return mit
1
getchar()
wegwerfen

von Dirk B. (dirkb2)


Lesenswert?

Das getchar überliest aber nur ein Zeichen.
Das Leerzeichen im Formatstring überliest aber alle 
(hintereinanderfolgende) Whitespace

von Rainer V. (rudi994)


Lesenswert?

Yalu X. schrieb:
> Man nimmt dann lieber getchar

Das nehm ich auch ganz gerne.
1
int main(void)
2
{
3
    char x, y, z;
4
5
    printf("Bitten geben Sie das 1.Zeichen ein: \n");
6
    do { scanf("%c",&x); }
7
    while (getchar() != '\n');
8
    printf("Bitten geben Sie das 2.Zeichen ein: \n");
9
    do { scanf("%c",&y); }
10
    while (getchar() != '\n');
11
    printf("Bitten geben Sie das 3.Zeichen ein: \n");
12
    do { scanf("%c",&z); }
13
    while (getchar() != '\n');
14
    return 0;
15
}

von Karl H. (kbuchegg)


Lesenswert?

Yalu X. schrieb:

>> fflush ist eigentlich nur für Ausgabe-Streams definiert. Was passiert,
>> wenn man fflush auf einen Eingabe-Stream anwendet ist
>> implementierungs-abhängig.
>
> Schlimmer noch: Es ist sogar undefined Behavior.


War mir nicht mehr sicher.

> Man nimmt dann lieber getchar (zum Einlesen einzelner Zeichen) oder
> fgetc (zum Einlesen ganzer Zeilen).

Du meinst wohl fgets

So haben wir das im Grunde auch meistens gehandhabt. Mit fgets eine 
Zeile einlesen und dann mit sscanf oder sonstigen Mittel zerlegen. scanf 
bzw. fscanf können zwar benutzt werden, wenn man genau definierte 
Eingaben hat, aber im Grunde ist eine fgets/parsing Kombination meistens 
sicherer.
Wobei sich mit den 'neueren' Formatieroptionen da auch einiges geändert 
hat. Aber wie es so schön heisst: Ein alter Hund lernt keine neuen 
Tricks mehr. "fgets, dann parsing" hat sich bei mir bewährt. Sofern 
nicht überhaupt plattformspezifische Methoden zum Zug kommen müssen, 
weil so Dinge wie Funktionstasten od. Cursortasten da auch noch 
mitgenommen werden müssen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl Heinz schrieb:
> Du meinst wohl fgets

Ja, danke für die Korrektur.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.