Ich habe folgendes Programm:
int main(int argc, char *argv[])
{
int a, b;
char c1, c2;
printf("Enter something: ");
scanf("%d", &a); // line 1
printf("Enter other something: ");
scanf("%d", &b); // line 2
printf("Enter a char: ");
scanf("%c", &c1); // line 3
printf("Enter another char: ");
scanf("%c", &c2); // line 4
printf("Done"); // line 5
system("PAUSE");
return 0;
}
Wie ich im C-Buch gelesen habe, sagt der Autor das scanf()
hat ein Zeilenumbruchzeichen im Puffer hinterlassen, daher stoppt das Programm nicht bei Zeile 4, damit der Benutzer die Daten eingeben kann, sondern speichert das Zeilenumbruchzeichen darin c2
und springt zu Zeile 5.
Ist das richtig?
Dies geschieht jedoch nur mit char
Datentypen? Denn ich habe dieses Problem nicht mit gesehen int
Datentypen wie in Zeile 1, 2, 3. Stimmt das?
Das scanf()
Die Funktion überspringt führende Leerzeichen automatisch, bevor sie versucht, andere Konvertierungen als Zeichen zu analysieren. Die Zeichenformate (hauptsächlich %c
; auch Scan-Sets %[…]
– und %n
) sind die Ausnahme; Sie überspringen keine Leerzeichen.
Verwenden " %c"
mit einem führenden Leerzeichen, um optionale Leerzeichen zu überspringen. Verwenden Sie kein nachgestelltes Leerzeichen in a scanf()
Zeichenfolge formatieren.
Beachten Sie, dass dies immer noch keine abschließenden Leerzeichen im Eingabestrom verbraucht, nicht einmal bis zum Ende einer Zeile. Achten Sie also darauf, wenn Sie es auch verwenden getchar()
oder fgets()
auf dem gleichen Eingabestrom. Wir bringen gerade scanf dazu, Leerzeichen zu überspringen Vor Konvertierungen, wie es für %d
und andere Konvertierungen, die keine Zeichen sind.
Beachten Sie, dass Nicht-Leerzeichen-“Anweisungen” (to use POSIX-Scanf-Terminologie) außer Konvertierungen, wie der wörtliche Text in scanf("order = %d", &order);
überspringt auch keine Leerzeichen. Das wörtliche order
muss mit dem nächsten zu lesenden Zeichen übereinstimmen.
Also willst du wahrscheinlich " order = %d"
dort, wenn Sie einen Zeilenumbruch aus der vorherigen Zeile überspringen möchten, aber dennoch eine wörtliche Übereinstimmung mit einer festen Zeichenfolge benötigen, wie diese Frage.
Eine andere Option (die ich von bekommen habe hier) besteht darin, den Zeilenumbruch mithilfe von zu lesen und zu verwerfen Option zur Unterdrückung von Zuweisungen. Dazu setzen wir einfach ein Format zum Lesen eines Zeichens mit einem Sternchen dazwischen %
und c
:
scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3
scanf
liest dann das nächste Zeichen (also den Zeilenumbruch), weist es aber keinem Zeiger zu.
Am Ende würde ich aber Zweiter werden die letzte Option der FAQ:
Oder Sie können je nach Ihren Anforderungen auch scanf()/getchar() vergessen und fgets() verwenden, um eine Textzeile vom Benutzer zu erhalten und sie selbst zu analysieren.
Verwenden getchar()
vor dem zweiten Anruf scanf()
.
scanf("%c", &c1);
getchar(); // <== remove newline
scanf("%c", &c2);
Um zu wiederholen, was ich in einer anderen Antwort zu C++ gepostet habe: Ich schlage vor, zu werfen scanf()
weg, um es nie zu verwenden und stattdessen zu verwenden fgets()
und sscanf()
.
Der Grund dafür ist, dass zumindest in Unix-ähnlichen Systemen standardmäßig das Terminal, auf dem Ihr CLI-Programm läuft, einige Benutzereingaben verarbeitet, bevor Ihr Programm sie sieht. Es puffert die Eingabe, bis eine neue Zeile eingegeben wird, und ermöglicht eine rudimentäre Zeilenbearbeitung, z. B. die Rücktaste zum Laufen zu bringen.
Sie können also nie ein einzelnes Zeichen auf einmal oder ein paar einzelne Zeichen, sondern nur eine ganze Zeile erhalten. Aber darum geht es zB scanf("%d")
verarbeitet, stattdessen verarbeitet es nur die Ziffern, und hält dort anwobei der Rest für eine Zukunft in der C-Bibliothek gepuffert bleibt stdio
Funktion zu verwenden. Wenn Ihr Programm z
printf("Enter a number: ");
scanf("%d", &a);
printf("Enter a word: ");
scanf("%s", word);
und Sie betreten die Linie 123 abcd
es ist abgeschlossen beide scanf()
s sofort, aber erst nach einem Zeilenumbruch. Der Erste scanf()
kehrt nicht zurück, wenn ein Benutzer die Leertaste gedrückt hat, obwohl dort die Zahl endet (weil sich die Zeile zu diesem Zeitpunkt noch im Zeilenpuffer des Terminals befindet); und der zweite scanf()
wartet nicht darauf, dass Sie eine weitere Zeile eingeben (weil der Eingabepuffer bereits genug enthält, um die %s
Wandlung).
Das ist nicht das, was Benutzer normalerweise erwarten!
Stattdessen erwarten sie, dass das Drücken der Eingabetaste die Eingabe abschließt, und wenn Sie die Eingabetaste drücken, erhalten Sie entweder einen Standardwert oder einen Fehler, möglicherweise mit dem Vorschlag, bitte wirklich nur die Antwort zu geben.
Das kann man eigentlich nicht mit scanf("%d")
. Wenn der Benutzer nur die Eingabetaste drückt, passiert nichts. Da scanf()
wartet noch auf die Nummer. Das Terminal sendet die Zeile weiter, aber Ihr Programm sieht sie nicht, weil scanf()
isst es. Sie haben keine Möglichkeit, auf den Fehler des Benutzers zu reagieren.
Das ist auch nicht sehr sinnvoll.
Daher empfehle ich die Verwendung fgets()
oder getline()
um jeweils eine ganze Eingabezeile zu lesen. Dies entspricht genau dem, was das Terminal gibt, und gibt Ihrem Programm immer die Kontrolle, nachdem der Benutzer eine Zeile eingegeben hat. Was Sie mit der Eingabezeile machen, ist Ihnen überlassen, wenn Sie eine Zahl wollen, können Sie sie verwenden atoi()
, strtol()
oder auch sscanf(buf, "%d", &a)
um die Nummer zu parsen. sscanf()
hat nicht die gleiche Diskrepanz wie scanf()
da der Puffer, aus dem gelesen wird, in der Größe begrenzt ist, und wenn er endet, endet er – die Funktion kann nicht auf mehr warten.
(fscanf()
in einer regulären Datei kann auch in Ordnung sein, wenn das Dateiformat unterstützt, wie es Zeilenumbrüche wie alle Leerzeichen überfliegt. Für zeilenorientierte Daten würde ich noch verwenden fgets()
und sscanf()
.)
Verwenden Sie also anstelle von dem, was ich oben hatte, Folgendes:
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
sscanf(buf, "%d", &a);
oder überprüfen Sie tatsächlich den Rückgabewert von sscanf()
auch, damit Sie leere Zeilen und ansonsten ungültige Daten erkennen können:
#include <stdio.h>
int main(void)
{
const int bufsize = 100;
char buf[bufsize];
int a;
int ret;
char word[bufsize];
printf("Enter a number: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%d", &a);
if (ret != 1) {
fprintf(stderr, "Ok, you don't have to.\n");
return 1;
}
printf("Enter a word: ");
fgets(buf, bufsize, stdin);
ret = sscanf(buf, "%s", word);
if (ret != 1) {
fprintf(stderr, "You make me sad.\n");
return 1;
}
printf("You entered %d and %s\n", a, word);
}
Wenn Sie möchten, dass das Programm darauf besteht, können Sie natürlich eine einfache Funktion zum Schleifen erstellen fgets()
und sscanf()
bis der Benutzer sich dazu herablässt, das zu tun, was ihm gesagt wird; oder einfach sofort mit einem Fehler zu beenden. Hängt davon ab, was Ihr Programm Ihrer Meinung nach tun sollte, wenn der Benutzer nicht mitspielen möchte.
Sie könnten etwas Ähnliches tun, zB durch Schleifen getchar()
um Zeichen bis zu einem Zeilenumbruch danach zu lesen scanf("%d")
zurückgegeben, wodurch der im Puffer verbliebene Müll gelöscht wird, aber das ändert nichts an dem Fall, in dem der Benutzer nur die Eingabetaste in einer leeren Zeile drückt. Wie auch immer, fgets()
würde bis zu einem Zeilenumbruch lesen, also müssen Sie es nicht selbst tun.
Zwei Problemumgehungen. Man fängt den zusätzlichen Leerraum trickreich ein.
getchar(); // (1) add `getchar()`
scanf("%c", &c1);
scanf(" %c", &c1); // (2) add whitespace before %c
Der andere benutzt fgets()
stattdessen scanf()
. Notiz: gets()
ist unsicher, sehen Sie, warum gets gefährlich ist
Dagegen würde ich eher den zweiten Weg empfehlen. Weil es mehr ist lesbar und wartbar. Zum Beispiel müssen Sie zunächst eine Weile nachdenken, bevor Sie einige hinzufügen / verschieben scanf()
.
/*Take char input using scanf after int input using scanf just use fflush(stdin) function after int input */
#include<stdio.h>
#include<conio.h>
void main()
{
int x;
char y;
clrscr();
printf(" enter an int ");
scanf("%d",&x);
fflush(stdin);
printf("\n Now enter a char");
scanf("%c",&y);
printf("\n X=%d and Y=%c",x,y);
getch();
}
Das wird manchmal suggeriert
fflush(stdin)
kann vor dem Aufruf verwendet werdenscanf()
für ein einzelnes Zeichen. Bitte lesen Sie Verwendenfflush(stdin)
für eine Diskussion der Vor- und Nachteile und Alternativen zu dieser Methode (die mehr oder weniger unter Windows funktioniert und an den meisten anderen Orten nicht funktioniert).– Jonathan Leffler
2. Dezember 2018 um 3:09 Uhr
Könnten Sie uns bitte mitteilen, auf welches Buch Sie sich beziehen?
– Suryakiran
31. März 2020 um 22:17 Uhr
@JonathanLeffler Verwenden
fflush(stdin)
ist schlicht undefiniertes Verhalten.– Zakk
22. Mai um 8:32 Uhr