Die einfachste Möglichkeit ist, einen führenden Platz auf das Format-String in scanf()
hinzuzufügen. Zum Beispiel, wenn Sie den Benutzer auffordern wollen ein Zeichen erneut eingeben, wenn schlechter Eingang versehen ist, können Sie dies tun: obwohl
#include <stdio.h>
int main(void)
{
char tmp;
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
while (scanf(" %c", &tmp) != 1 || (tmp != 'p' && tmp != 's')) {
printf("Please enter \"p\" or \"s\": ");
}
printf("You chose %c\n", tmp);
printf("Choose \"a\"scending or \"d\"escending sort: ");
while (scanf(" %c", &tmp) != 1 || (tmp != 'a' && tmp != 'd')) {
printf("Please enter \"a\" or \"d\": ");
}
printf("You chose %c\n", tmp);
return 0;
}
Es gibt ein Problem mit diesem einfachen Ansatz. Wenn der Benutzer "aaap"
tritt dann würde die resultierende Ausgabe sein:
Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: aaap
Please enter "p" or "s": Please enter "p" or "s": Please enter "p" or "s": You chose p
Choose "a"scending or "d"escending sort:
Oder, schlimmer noch, wenn der Benutzer ein richtiges erstes Zeichen von anderen Zeichen gefolgt eintritt, wie "sfffa"
:
Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: sfffa
You chose s
Choose "a"scending or "d"escending sort: Please enter "a" or "d": Please enter "a" or "d": Please enter "a" or "d": You chose a
Weil scanf()
Blätter Nicht übereinstimmende Zeichen im Eingabestrom müssen vor dem erneuten Lesen des Eingabestreams behandelt werden. In der einfachen Lösung, die " %c"
verwendet, verursacht der anfängliche Raum scanf()
, führende Leerzeichen zu überspringen, aber alle anderen Zeichen werden aufgenommen. Und in der realen Welt kann man nicht darauf zählen, dass der Benutzer kooperiert, indem er sich gut benimmt.
Eine typische, tragbar, ist getchar()
Lösung in einer Schleife zu verwenden, um den Eingangsstrom nach einer Eingabeoperation zu löschen. Möglicherweise müssen Sie die Eingangsschleife Logik ein wenig ändern, dies zu tun:
#include <stdio.h>
...
char tmp;
int c;
int scanf_ret;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
scanf_ret = scanf("%c", &tmp);
while ((c = getchar()) != '\n' && c != EOF) {
continue; // discard extra characters
}
} while (scanf_ret != 1 || (tmp != 'p' && tmp != 's'));
Nach dem Aufruf von scanf()
wird eine Schleife eingetreten, dass liest und verwirft alle Zeichen im Eingabestrom verbleiben. Beachten Sie, dass c
ein int
ist, und dass EOF
explizit in der Schleife getestet wird. Die getchar()
Funktion kann im Fehlerfall EOF
zurückgeben, oder wenn der Benutzer über die Tastatur EOF
signalisiert, oder wenn die Eingabe von einer Datei umgeleitet wurde. Wenn in solchen Fällen kein Test für EOF
ausgeführt wird, würde dies zu einer Endlosschleife führen.
Diese Lösung funktioniert, auch wenn der Benutzer nicht vorhersehbar Eingang eintritt, da der Eingangsstrom immer nach der Eingabe immer gelöscht wird.Beachten Sie, dass das führende Leerzeichen in der Formatzeichenfolge nicht benötigt wird. Eine andere Lösung wäre, fgets()
zu verwenden, um eine Eingabezeile in einen Puffer zu lesen, und sscanf()
, um den Puffer zu analysieren.
#include <stdio.h>
#include <stdlib.h>
...
char buffer[1000];
char tmp;
int sscanf_ret;
do {
printf("Enter \"p\" if you want to sort and shuffle a list of players "
"or enter \"s\" if you want to sort a list of slots: "); // prompt
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
fprintf(stderr, "Error in fgets()\n");
exit(EXIT_FAILURE);
}
sscanf_ret = sscanf(buffer, "%c", &tmp);
} while (sscanf_ret != 1 || (tmp != 'p' && tmp != 's'));
Die fgets()
Funktion ruft eine ganze Zeile der Eingabe, die durch die Neuen-Zeile oder erhält (sizeof buffer) - 1
Zeichen aus dem Eingangsstrom, wenn der Puffer nicht groß genug ist, um alle Zeichen in dem Eingangsstrom zu halten, und ein \0
terminator . Durch die Bereitstellung eines großzügig bemessenen Puffers sollte dies gut funktionieren. Ein Benutzer könnte immer noch eine sehr große Anzahl von Zeichen eingeben, und während dies den Puffer nicht überläuft, würden Zeichen in dem Eingabestrom für die nächste Leseoperation zurückbleiben, um zu finden. Um solche Probleme zu vermeiden, wenn gibt es zusätzliche Zeichen zurückgelassen, sollte der Eingangsstrom nach dem Aufruf an fgets()
mit der getchar()
Schleife von oben gelöscht werden.
Alle diese Techniken haben ihren Platz. Offensichtlich sind die letzten beiden viel robuster als die ersten, und da es unwahrscheinlich ist, dass ein Benutzer 999 Zeichen an der Eingabeaufforderung (einschließlich der Zeilenschaltung) eingibt, sind sie fast gleichberechtigt. Um alle Überraschungen zu vermeiden, ist es jedoch am besten, den Eingabestream nach der Benutzereingabe explizit zu löschen.
Haben Sie ein grundlegendes Debugging durchgeführt? Wie mit einem Debugger und/oder debug print-Anweisungen, um herauszufinden, welche Werte tmp nimmt, wenn es funktioniert und wann nicht? – kaylum
Es funktioniert gut auf meinem System. – Jarvis
Was hast du vorher gelesen? Hat es einen Zeilenumbruch in der Eingabe hinterlassen? Sie sollten 'if (scanf ("% c ", & tmp)! = 1) {... handle error ...}' in Betracht ziehen, wo das Leerzeichen vor dem '% c' kein Zufall ist - es überspringt Leerzeichen (Leerzeichen, Tabs, Zeilenumbrüche) und wartet darauf, dass etwas anderes (ein 'p' oder ein' s') eingegeben wird. –