Es soll unter Linux ein double Wert per Tastatur eingegeben werden und das führt zu Problemen
-
Folgende Problemstellung:
Ich versuche in einem Konsolenprogramm den Benutzer nach einer Zahl vom Typ double zu fragen, der Benutzer soll die Zahl eingeben und die Eingabe mit Enter abschließen.
Wenn der Benutzer aber einfach nur die Entertaste drückt oder etwas anderes als eine Zahl eingibt, dann soll das Programm weiterhin auf eine Eingabe einer Zahl warten.
Erst wenn eine Zahl eingegeben wurde, die auch 0 bzw. 0.0 sein darf, soll das Programm weiter machen.Und hier ist das Problem, wenn ich den double Wert mit der scanf() Funktion abfrage (siehe Variante 2), dann wird keine Eingabe einer Zahl mehr akzeptiert,
falls zuvor keine Zahl eingegeben wurde.
D.h. das Programm kann nicht mehr weiterarbeiten und bleibt in der Schleife hängen.Bei Variante 1 gibt es dieses Problem nicht. Allerdings wird hier die Schleife
verlassen, sobald der Nutzer nur die Return Taste drückt.
In diesem Fall ist der Wert dann automatisch 0.0, mit diesem wird dann weitergerechnet, was aber nicht sein soll.
Denn der Benutzer könnte sich ja vertippt haben.Ich habe daher auch schon versucht bei Variante 2 den Tastaturpuffer zu leeren,
aber diese Lösung mit fflush(stdin) ist gemäß C Standard so weit ich herausfinden konnte nicht definiert und funktioniert daher unter Linux auch nicht.Bei Variante 1 habe ich versucht die Eingabe mit isdigit() irgendwie zu überprüfen, allerdings ist das logischerweise auf die double Variable "input" nicht anwendbar, da die sowieso eine Zahl ist.
Auch kann ich den Wert 0 nicht benutzen, um den Ausstieg aus der Schleife zu verhindern, denn es könnte ja sein, dass der Nutzer tatsächlich 0 eingeben möchte.
Und der Wert, den ich mit fgets im char Array line speichere, ist auch kaum zu gebrauchen, da der eben mehr als nur ein "\n" enthält und isdigit() darauf mit einem Speicherfehler reagiert. Da könnte ich meine sscanf Funktion auch selber schreiben.Natürlich könnte ich jetzt auch einfach einen String oder die Chars einzeln mit fgets einlesen und mir daraus meine Zahl selber bilden oder daraus mit sscanf einen String machen, dann kann ich auch isdigit() und atoi() darauf anwenden, aber wozu gibt's dann scanf und sscanf mit Formatierungsmöglichkeiten extra für Zahlen?
Das muss doch auch mit denen irgendwie zu machen sein, ohne das man die obigen Nebeneffekte hat.Hier der Code:
#include <stdio.h> #include <ctype.h> // C99 // Varinate 1: double eingabe(){ char line[100]; double input; do { fgets(line,sizeof(line),stdin); } while(!sscanf(line,"%lf",&input)); /* Problem: Wird nur ENTER aber keine Zahl eingegeben, dann wird die Schleife verlassen. */ return input; } // Variante 2: double eingabe_variante_2(){ double input; do { // fflush(stdin) // Dies ist nicht definiert, daher muss es unter Linux nicht funktionieren } while (!scanf("%lf",&input)); /* Problem: wurde zuvor etwas falsches eingegeben, z.B. keine Zahl, dann kann die Schleife dennoch nicht verlassen werden, wenn man eine Zahl anschließend eingibt, weil scanf noch aus einem alten Tastaturpuffer die Daten liest und dieser mit fflush(stdin) unter Linux auch nicht gelöscht werden kann, weil das gemäß C Standard nicht definiert ist. */ return input; } int main(){ double ergebnis; printf("Variante 1: Bitte geben sie eine Zahl ein: "); ergebnis = eingabe(); printf("Ergebnis = %lf", ergebnis); printf("\nVariante 2: Bitte geben sie eine weitere Zahl ein: "); ergebnis = eingabe_variante_2(); printf("Ergebnis = %lf\n", ergebnis); return 0; }
-
Das !scanf ist eins deiner Probleme
Variante 1:
do {... } while(1 != sscanf(line,"%lf",&input));
Variante 2:
https://www.c-plusplus.net/forum/p1146014#1146014Möglich ist auch
while (1 != scanf("%lf%*[^\n]",&input));
Alles ungetestet.
-
DirkB schrieb:
Das !scanf ist eins deiner Probleme
Variante 1:
do {... } while(1 != sscanf(line,"%lf",&input));
Danke, das funktioniert sogar.
Ich hätte nie gedacht, dass es an so etwas banalem liegen könnte.
Auch dachte ich bisher, dass
while ( true != irgendwas());gleichwertig ist mit
while(!irgendwas());
Wenn die Funktion irgendwas() einen Boolwert zurückliefert.
Zumindest ist das in anderen Sprachen meistens so.Warum funktioniert das in C99 hier nicht?
Also warum muss man das explizit ausschreiben?Variante 2:
https://www.c-plusplus.net/forum/p1146014#1146014Das muss ich mir noch durchlesen.
Möglich ist auch
while (1 != scanf("%lf%*[^\n]",&input));
Alles ungetestet.
Das hat leider nicht funktioniert, aber die 1 Variante funktioniert jetzt, danke dafür.
-
sscanf Problem schrieb:
Auch dachte ich bisher, dass
while ( true != irgendwas());gleichwertig ist mit
while(!irgendwas());
Wenn die Funktion irgendwas() einen Boolwert zurückliefert.
Zumindest ist das in anderen Sprachen meistens so.Warum funktioniert das in C99 hier nicht?
Also warum muss man das explizit ausschreiben?Du schnappst/suchst dir jetzt eine Referenz der C-Standardbibliothek und schaust dir die Bedeutung des Rückgabewerts von
scanf()
an!
-
Ich habe jetzt gerade mal getestet, was sscanf zurückliefert, wenn man nur Enter drückt.
printf("Testcode: %i\n", sscanf(line,"%lf",&input));
Das Ergebnis ist -1, wenn nur Enter eingegeben wurde.
Das Ergebnis ist 0, wenn ein Buchstabe oder irgendwetwas anderes außer Zahlen eingegeben wurde.
Und 1, wenn eine Zahl eingegeben wurde.Daher würde ich darauf schließen, dass -1 für C99 nicht FALSE ist.
Es wird also als TRUE interpretiert.
Ist das so im C Standard definiert?
-
Swordfish schrieb:
sscanf Problem schrieb:
Auch dachte ich bisher, dass
while ( true != irgendwas());gleichwertig ist mit
while(!irgendwas());
Wenn die Funktion irgendwas() einen Boolwert zurückliefert.
Zumindest ist das in anderen Sprachen meistens so.Warum funktioniert das in C99 hier nicht?
Also warum muss man das explizit ausschreiben?Du schnappst/suchst dir jetzt eine Referenz der C-Standardbibliothek und schaust dir die Bedeutung des Rückgabewerts von
scanf()
an!Ja, so wie es aussieht ein signed int.
Aber war es nicht so, dass alles <= 1 als FALSE zu interpretieren ist?
-
sscanf Problem schrieb:
Ja, so wie es aussieht ein signed int.
Aber war es nicht so, dass alles <= 1 als FALSE zu interpretieren ist?sry, ich meinte < 1.
1 ist ja wieder logsicherweise TRUE.
-
sscanf Problem schrieb:
Aber war es nicht so, dass alles <= 1 als FALSE zu interpretieren ist?
Nein. Alles ungleich
0
isttrue
.Du hast nicht verstanden.
Swordfish schrieb:
Du schnappst/suchst dir jetzt eine Referenz der C-Standardbibliothek und schaust dir die Bedeutung des Rückgabewerts von
scanf()
an!Wer sagt, daß das irgendwas mit einem Boolean zu tun hat!?
-
(s)scanf gibt keinen Boolwert zurück, sondern die Anzahl der erfolgreich gelesenen Elemente. Oder auch EOF bei einem Lesefehler.
Für sscanf ist eine Leerzeile ein Lesefehler. EOF ist etwas anderes als 0.Bei
while (1 != scanf("%lf%*[^\n]",&input));
ist das auch nur eine while-Schleife. Keine do-while.
~Ich weiß allerdings nicht, ob die 1 richtig ist.~
-
swordfish schrieb:
Nein. Alles ungleich 0 ist true.
Okay, dann lag ich hier im Irrtum und das erklärt dann auch das Verhalten des Codes.
Bei
while (1 != scanf("%lf%*[^\n]",&input));
ist das auch nur eine while-Schleife. Keine do-while.
~Ich weiß allerdings nicht, ob die 1 richtig ist.~Ich habe mir jetzt den verlinkten Thread durchgelesen und Variante 2 entsprechend modifiziert.
So funktioniert es:
// Variante 2: double eingabe_variante_2(){ double input; int c; do { c; // int c, nicht char c. while ((c = getchar()) != EOF && c != '\n'); // das ist jetzt mein flush() } while (!scanf("%lf",&input)); return input; }
-
Komisch. Bei deiner "Lösung" muss ich bevor ich eine
double
eingeben kann einmal Enter drücken?? und wenn ich dannEOF
eingebe, komm ich trotzdem aus der Schleife, ohne einedouble
eingegeben zu haben?
-
sscanf Problem schrieb:
So funktioniert es:
// Variante 2: double eingabe_variante_2(){ double input; int c; do { c; // int c, nicht char c. while ((c = getchar()) != EOF && c != '\n'); // das ist jetzt mein flush() } while (!scanf("%lf",&input)); return input; }
Ich muss mich korrigieren, es funktioniert nicht, wenn gleich am Anfang eine Zahl richtig eingegeben wurde.
Habe es in:
// Variante 2: double eingabe_variante_2(){ double input; int c; do { c; // int c, nicht char c. while ((c = getchar()) != EOF && c != '\n'); // Dies funktioniert im Gegensatz zu fflush(stdin) // } while (!scanf("%lf",&input)); } while (1 != scanf("%lf",&input)); // } while (1 != scanf("%lf%*[^\n]",&input)); return input; }
und
// Variante 2: double eingabe_variante_2(){ double input; int c; do { c; // int c, nicht char c. while ((c = getchar()) != EOF && c != '\n'); // Dies funktioniert im Gegensatz zu fflush(stdin) // } while (!scanf("%lf",&input)); // } while (1 != scanf("%lf",&input)); } while (1 != scanf("%lf%*[^\n]",&input)); return input; }
geändert, aber bei beiden Varianten existiert der Fehler, dass man die Zahl, wenn sie am Anfang richtig eingegeben wurde, noch einmal eingeben muss.
Variante 2 ist somit immer noch ungelöst.
Wenn auch der erste Bug, dass das Programm nicht mehr weitergeht, sobald irgendetwas anderes als eine Zahl eingegeben wurde, bei beiden modifizierten Varianten nicht mehr vorkommt.
-
Swordfish schrieb:
Komisch. Bei deiner "Lösung" muss ich bevor ich eine
double
eingeben kann einmal Enter drücken??Ja, das ist leider der neue Bug, der betrifft aber alle Modifikationen von Variante 2.
Also egal welches man davon nimmt:
} while (!scanf("%lf",&input)); } while (1 != scanf("%lf",&input)); } while (1 != scanf("%lf%*[^\n]",&input));
Der Fehler trifft bei allen auf.
Sowohl wenn man zuerst ENTER eingibt oder eine ZAHL, also richtig, eingibt, muss man die eigentliche Zahl noch einmal geben.Lediglich der andere Bug, dass es gar nicht mehr weitergeht, wenn man einen Buchstaben oder ENTER zuerst eingegeben hat ist jetzt weg.
-
Dann mach eine while-Schleife (das do muss weg).
-
Wie wäre es, wenn du dich von der Vorstellung zwei Varianten zu brauchen verabschiedest und stattdessen die logische Vorgehensweise
- Leseversuch
- wenn Fehler:
-- Fehlerbehandlung
anwendest?
-
Swordfish schrieb:
Wie wäre es, wenn du dich von der Vorstellung zwei Varianten zu brauchen verabschiedest und stattdessen die logische Vorgehensweise
- Leseversuch
- wenn Fehler:
-- Fehlerbehandlung
anwendest?Das ist doch gar nicht das Thema.
Variante 1 funktioniert, ich könnte diese also nehmen.
Aber Variante 2 funktioniert noch nicht.
Was bitteschön ist jetzt falsch daran zu untersuchen warum sie nicht geht und sie entsprechend zu modifizieren bis sie geht?Variante 1 verwendet sscanf, Variante 2 scanf, das ist schon ein großer Unterschied und vielleicht braucht man mal das eine oder das andere.
Bisher existiert aber nur eine funktionierende Lösung für sscanf.
Die für scanf muss erst noch gefunden werden.@DirkB
Werde ich noch testen.
-
Okay, die while(){} Variante ohne do geht:
// Variante 3: double eingabe_variante_3(){ double input; int c; while (1 != scanf("%lf",&input)){ // while (1 != scanf("%lf%*[^\n]",&input)) { // <- das war nicht nötig c; // int c, nicht char c. while ((c = getchar()) != EOF && c != '\n'); // Dies funktioniert im Gegensatz zu fflush(stdin) } return input; }
-
sscanf Problem schrieb:
Okay, die while(){} Variante ohne do geht:
Und das ist genau:
Swordfish schrieb:
- Leseversuch
- wenn Fehler:
-- FehlerbehandlungWas meinst du, was Zeile 7 macht?
Die schleppst du jetzt schon ein paar Versionen mit dir rum.
-
DirkB schrieb:
Was meinst du, was Zeile 7 macht?
Steht doch im Kommentar daneben!?
-
Was noch geht, ohne extra Variable oder Puffer:
while (1 != scanf("%lf", &input)) scanf("%*[^\n]"); // im Fehlerfall alles bis zum '\n' überlesen scanf("%*[^\n]"); // evtl Rest bis zum '\n' überlesen
Der Versuch, das mit einem
scanf
zu machen geht nicht, dascanf
mit dem einlesen aufhört, wenn es auf einen Fehler trifft.