In dieser Antwort gehe ich davon aus, dass Sie Textzeilen lesen und interpretieren . Vielleicht fordern Sie den Benutzer auf, der etwas eingibt und RETURN drückt. Oder Sie lesen Zeilen mit strukturiertem Text aus einer Datendatei.
Da Sie Textzeilen lesen, ist es sinnvoll, Ihren Code um eine Bibliotheksfunktion herum zu organisieren, die eine Textzeile liest. Die Standardfunktion ist fgets()
, obwohl es andere gibt (einschließlich getline
). Und dann ist der nächste Schritt, diese Textzeile irgendwie zu interpretieren.
Hier ist das Grundrezept für den Aufruf fgets
zum Lesen einer Textzeile:
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
Dies liest einfach eine Textzeile ein und druckt sie wieder aus. Wie geschrieben hat es ein paar Einschränkungen, die wir in einer Minute erreichen werden. Es hat auch eine sehr gute Funktion: Diese Zahl 512, an die wir als zweites Argument übergeben haben, fgets
ist die Größe des Arrays, in das
line
wir fgets
einlesen möchten. Diese Tatsache - dass wir erkennen können, fgets
wie viel es lesen darf - bedeutet, dass wir sicher sein können, dass fgets
das Array nicht überläuft , indem wir zu viel hineinlesen.
Jetzt wissen wir also, wie man eine Textzeile liest, aber was ist, wenn wir wirklich eine Ganzzahl oder eine Gleitkommazahl oder ein einzelnes Zeichen oder ein einzelnes Wort lesen möchten? (Das heißt, was ist, wenn der
scanf
Anruf wir versuchen , auf zu verbessern war wie ein Formatbezeichner mit %d
, %f
, %c
, oder %s
?)
Es ist einfach, eine Textzeile - eine Zeichenfolge - als eines dieser Dinge neu zu interpretieren. Um eine Zeichenfolge in eine Ganzzahl umzuwandeln, ist der einfachste (wenn auch unvollständige) Weg, dies zu tun, der Aufruf atoi()
. Um in eine Gleitkommazahl umzuwandeln, gibt es atof()
. (Und es gibt auch bessere Möglichkeiten, wie wir gleich sehen werden.) Hier ein sehr einfaches Beispiel:
printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);
Wenn Sie möchten, dass der Benutzer ein einzelnes Zeichen eingibt (möglicherweise y
oder
n
als Ja / Nein-Antwort), können Sie buchstäblich nur das erste Zeichen der Zeile wie folgt abrufen:
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
(Dies ignoriert natürlich die Möglichkeit, dass der Benutzer eine Antwort mit mehreren Zeichen eingegeben hat. Alle zusätzlichen Zeichen, die eingegeben wurden, werden stillschweigend ignoriert.)
Wenn Sie möchten, dass der Benutzer eine Zeichenfolge eingibt, die definitiv kein Leerzeichen enthält, wenn Sie die Eingabezeile behandeln möchten
hello world!
Da die Zeichenfolge "hello"
von etwas anderem gefolgt wird (was das scanf
Format %s
getan hätte), ist es in diesem Fall nicht ganz so einfach, die Zeile auf diese Weise neu zu interpretieren, also die Antwort darauf Ein Teil der Frage muss etwas warten.
Aber zuerst möchte ich auf drei Dinge zurückkommen, die ich übersprungen habe.
(1) Wir haben angerufen
fgets(line, 512, stdin);
in das Array einzulesen line
, und wo 512 die Größe des Arrays ist line
, fgets
weiß also, dass es nicht überlaufen soll. Um sicherzustellen, dass 512 die richtige Nummer ist (insbesondere um zu überprüfen, ob möglicherweise jemand das Programm optimiert hat, um die Größe zu ändern), müssen Sie zurücklesen, wo line
immer dies deklariert wurde. Das ist ein Ärgernis, daher gibt es zwei viel bessere Möglichkeiten, um die Größen synchron zu halten. Sie können (a) den Präprozessor verwenden, um einen Namen für die Größe zu erstellen:
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
Oder (b) verwenden Sie den sizeof
Operator von C :
fgets(line, sizeof(line), stdin);
(2) Das zweite Problem ist, dass wir nicht nach Fehlern gesucht haben. Wenn Sie Eingaben lesen, sollten Sie immer nach Fehlern suchen. Wenn fgets
die von Ihnen angeforderte Textzeile aus irgendeinem Grund nicht gelesen werden kann, wird dies durch die Rückgabe eines Nullzeigers angezeigt. Also hätten wir Dinge wie tun sollen
printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
printf("Well, never mind, then.\n");
exit(1);
}
Schließlich gibt es das Problem, dass zum Lesen einer Textzeile
fgets
Zeichen gelesen und in Ihr Array eingefügt werden, bis das \n
Zeichen gefunden wird, das die Zeile beendet, und das \n
Zeichen auch in Ihr Array eingefügt wird . Sie können dies sehen, wenn Sie unser früheres Beispiel geringfügig ändern:
printf("you typed: \"%s\"\n", line);
Wenn ich dies ausführe und "Steve" eingebe, wenn es mich dazu auffordert, wird es ausgedruckt
you typed: "Steve
"
Das "
in der zweiten Zeile ist, weil die Zeichenfolge, die es gelesen und wieder ausgedruckt hat, tatsächlich war "Steve\n"
.
Manchmal spielt diese zusätzliche Zeile keine Rolle (wie bei unserem Anruf
atoi
oder atof
, da beide zusätzliche nicht numerische Eingaben nach der Nummer ignorieren), aber manchmal ist sie sehr wichtig. So oft wollen wir diese neue Zeile entfernen. Es gibt verschiedene Möglichkeiten, die ich in einer Minute erreichen werde. (Ich weiß, dass ich das viel gesagt habe. Aber ich werde auf all diese Dinge zurückkommen, das verspreche ich.)
An diesem Punkt denken Sie vielleicht: "Ich dachte, Sie sagten, es scanf
sei nicht gut, und dieser andere Weg wäre so viel besser. Aber fgets
es fängt an, wie ein Ärgernis auszusehen. Das Anrufen scanf
war so einfach ! Kann ich es nicht weiter benutzen?" ""
Sicher, Sie können weiter verwenden scanf
, wenn Sie möchten. (Und für wirklich
einfache Dinge ist es in gewisser Weise einfacher.) Aber bitte kommen Sie nicht zu mir, wenn es Ihnen aufgrund einer seiner 17 Macken und Schwächen versagt oder aufgrund Ihrer Eingabe in eine Endlosschleife gerät nicht erwartet, oder wenn Sie nicht herausfinden können, wie man es benutzt, um etwas komplizierteres zu tun. Und werfen wir einen Blick auf fgets
die tatsächlichen Belästigungen:
Sie müssen immer die Arraygröße angeben. Nun, das ist natürlich überhaupt kein Ärgernis - das ist eine Funktion, denn Pufferüberlauf ist eine wirklich schlechte Sache.
Sie müssen den Rückgabewert überprüfen. Eigentlich ist das eine Wäsche, denn um scanf
richtig zu verwenden , muss man auch den Rückgabewert überprüfen.
Sie müssen den \n
Rücken abstreifen . Ich gebe zu, das ist ein echtes Ärgernis. Ich wünschte, es gäbe eine Standardfunktion, auf die ich Sie hinweisen könnte, die dieses kleine Problem nicht hatte. (Bitte niemand ansprechen gets
.) Aber im Vergleich zu scanf's
17 verschiedenen Belästigungen werde ich diese eine Belästigung eines fgets
jeden Tages nehmen.
So , wie Sie Ihnen die Newline - Streifen? Drei Wege:
(a) Offensichtlicher Weg:
char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';
(b) Kniffliger und kompakter Weg:
strtok(line, "\n");
Leider funktioniert dieser nicht immer.
(c) Ein anderer kompakter und leicht dunkler Weg:
line[strcspn(line, "\n")] = '\0';
Und jetzt, da das nicht im Weg ist, können wir zu einer anderen Sache zurückkehren, die ich übersprungen habe: den Unvollkommenheiten von atoi()
und atof()
. Das Problem bei diesen ist, dass sie Ihnen keinen nützlichen Hinweis auf Erfolg oder Misserfolg geben: Sie ignorieren nachfolgende nicht numerische Eingaben stillschweigend und geben stillschweigend 0 zurück, wenn überhaupt keine numerische Eingabe vorhanden ist. Die bevorzugten Alternativen - die auch bestimmte andere Vorteile haben - sind strtol
und strtod
.
strtol
Sie können auch eine andere Basis als 10 verwenden, was bedeutet, dass Sie den Effekt (unter anderem) %o
oder %x
mit erzielen könnenscanf
. Aber zu zeigen, wie man diese Funktionen richtig einsetzt, ist eine Geschichte für sich und würde zu sehr von dem ablenken, was sich bereits in eine ziemlich fragmentierte Erzählung verwandelt. Deshalb werde ich jetzt nichts mehr darüber sagen.
Der Rest der Haupterzählung betrifft Eingaben, die Sie möglicherweise analysieren möchten und die komplizierter sind als nur eine einzelne Zahl oder ein einzelnes Zeichen. Was ist, wenn Sie eine Zeile lesen möchten, die zwei Zahlen oder mehrere durch Leerzeichen getrennte Wörter oder eine bestimmte Interpunktion enthält? Hier werden die Dinge interessant, und dort wurden die Dinge wahrscheinlich kompliziert, wenn Sie versuchten, Dinge mit zu tun scanf
, und wo es jetzt, da Sie eine Textzeile sauber gelesen haben, weitaus mehr Optionen gibt fgets
, obwohl die ganze Geschichte über all diese Optionen könnte wahrscheinlich ein Buch füllen, also werden wir hier nur die Oberfläche kratzen können.
Meine Lieblingstechnik besteht darin, die Zeile in durch Leerzeichen getrennte "Wörter" aufzuteilen und dann mit jedem "Wort" etwas weiter zu machen. Eine Hauptstandardfunktion hierfür ist
strtok
(die auch ihre Probleme hat und die auch eine ganze separate Diskussion bewertet). Meine eigene Präferenz ist eine dedizierte Funktion zum Erstellen eines Arrays von Zeigern auf jedes auseinandergebrochene "Wort", eine Funktion, die ich in
diesen Kursnotizen beschreibe . Auf jeden Fall , wenn Sie „Wörter“ haben, können Sie weiter jeden verarbeiten, vielleicht mit den gleichen atoi
/ atof
/ strtol
/ strtod
Funktionen haben wir bereits betrachtet.
Paradoxerweise besteht eine scanf
andere gute Möglichkeit, mit der Textzeile umzugehen, mit der wir gerade gelesen haben,
fgets
darin, sie weiterzugeben , obwohl wir hier ziemlich viel Zeit und Mühe aufgewendet haben, um herauszufinden, wie wir uns entfernen können sscanf
. Auf diese Weise erhalten Sie die meisten Vorteile scanf
, jedoch ohne die meisten Nachteile.
Wenn Ihre Eingabesyntax besonders kompliziert ist, kann es angebracht sein, eine "Regexp" -Bibliothek zu verwenden, um sie zu analysieren.
Schließlich können Sie die für Sie geeigneten Ad-hoc- Parsing-Lösungen verwenden. Sie können Zeichen char *
für Zeichen durch die Zeile bewegen, indem Sie mit einem
Zeiger nach den erwarteten Zeichen suchen. Oder Sie können mit Funktionen wie strchr
oder strrchr
, oder strspn
oder strcspn
oder nach bestimmten Zeichen suchen strpbrk
. Oder Sie können / convert analysieren und überspringen Gruppen von Ziffernzeichen , die mit strtol
oder
strtod
Funktionen , die wir über früher übersprungen.
Es gibt natürlich noch viel mehr zu sagen, aber hoffentlich bringt Ihnen diese Einführung den Einstieg.
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
der nachfolgende nicht numerische Text nicht als schlecht erkannt wird.