scanf() lässt das Newline-Zeichen im Puffer

Lesezeit: 10 Minuten

Benutzeravatar von ipkiss
ipkiss

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 wird manchmal suggeriert fflush(stdin) kann vor dem Aufruf verwendet werden scanf() für ein einzelnes Zeichen. Bitte lesen Sie Verwenden fflush(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

Benutzeravatar von Jeremiah Willcock
Jeremia Willcock

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.

  • %c, %n, %[] sind die 3 angegebenen Erwartungen, die keine führenden Leerzeichen verbrauchen.

    – chux – Wiedereinsetzung von Monica

    12. Dezember 2015 um 3:12 Uhr


  • @chux Also löscht der Scanf in anderen Fällen alle Leerzeichen im Puffer oder ignoriert sie für ihre Eingabe, aber sie sind immer noch da?

    – Suraj Jain

    19. Februar 2017 um 7:40 Uhr

  • @SurajJain Ja,

    – chux – Wiedereinsetzung von Monica

    20. Februar 2017 um 2:27 Uhr

  • Siehe Nachgestelltes Leerzeichen in scanf() Formatstring und scanf() fragt zweimal nach Eingaben, während ich erwarte, dass es nur einmal nach einer Diskussion über nachgestellte Leerzeichen in Formatzeichenfolgen fragt. Sie sind eine schlechte Idee – erstaunlich schlecht, wenn Sie menschliche Interaktion erwarten, und schlecht für die Programminteraktion.

    – Jonathan Leffler

    17. Oktober 2017 um 5:28 Uhr

  • Diese Antwort erklärt das sehr gut scanf() liest und dann formatiert, und nicht nur nach dem Format gelesen.

    – Priyanshul Govil

    12. April 2021 um 14:24 Uhr

Benutzeravatar von Shweta
Schweta

Verwenden scanf(" %c", &c2);. Dies wird Ihr Problem lösen.

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.

  • Das Problem bei dieser Technik ist, dass wenn der Benutzer tippt a dann Leerzeichen dann Zeilenumbruch, die unterdrückte Zeichenkonvertierung liest das Leerzeichen und lässt den Zeilenumbruch trotzdem. Wenn der Benutzer tippt supercalifragilisticexpialidocious wann du es erwartest a, müssen Sie sich um viele zusätzliche Charaktere kümmern. Sie können auch nie sagen, ob eine nachgestellte unterdrückte Conversion erfolgreich ist – sie werden nicht in der Rückgabe von gezählt scanf().

    – Jonathan Leffler

    12. Januar 2020 um 15:53 ​​Uhr

  • Das verwirft nur eines Charakter. Leerzeichen davor setzen % wird verwerfen jeder Betrag von führenden Leerzeichen (einschließlich keiner).

    – Wetterfahne

    25. Januar um 7:58 Uhr

Verwenden getchar() vor dem zweiten Anruf scanf().

scanf("%c", &c1);
getchar();  // <== remove newline
scanf("%c", &c2);

Benutzeravatar von ilkkachu
ilkkachu

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 abcdes 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.

  • fgets()/getline() und dann sscanf() oder strtol()etc., hat auch einen weiteren großen Vorteil gegenüber der Verwendung scanf() direkt. Wann das Programm auf unerwartete Eingaben stößt, bleibt der Eingabestrom nicht in einem unbekannten Zustand, in dem eine Wiederherstellung ohne Datenverlust unmöglich sein kann.

    – Andreas Henle

    18. Februar 2021 um 16:45 Uhr

  • Weitere Informationen zu Alternativen zu scanf: Was kann ich anstelle von scanf für die Eingabekonvertierung verwenden? Auch diese Anleitung kann hilfreich sein: Ein Anfängerleitfaden abseits von scanf()

    – Andreas Wenzel

    11. September um 15:35 Uhr

Benutzeravatar von wangloo
Wangloo

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().

  • fgets()/getline() und dann sscanf() oder strtol()etc., hat auch einen weiteren großen Vorteil gegenüber der Verwendung scanf() direkt. Wann das Programm auf unerwartete Eingaben stößt, bleibt der Eingabestrom nicht in einem unbekannten Zustand, in dem eine Wiederherstellung ohne Datenverlust unmöglich sein kann.

    – Andreas Henle

    18. Februar 2021 um 16:45 Uhr

  • Weitere Informationen zu Alternativen zu scanf: Was kann ich anstelle von scanf für die Eingabekonvertierung verwenden? Auch diese Anleitung kann hilfreich sein: Ein Anfängerleitfaden abseits von scanf()

    – Andreas Wenzel

    11. September um 15:35 Uhr

Benutzeravatar von Rai Singh
Rai Singh

/*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();
}

  • Beachten Sie die Vorbehalte zu Using fflush(stdin).

    – Jonathan Leffler

    12. April um 16:31 Uhr

  • conio.h ist nicht Teil von ISO C, sondern ein Plattform-spezifischer Header. Die Funktionen clrscr und getch sind ebenfalls plattformspezifisch. Beachten Sie, dass die Frage nicht mit einer bestimmten Plattform gekennzeichnet ist, sodass die Frage nicht für eine bestimmte Plattform gilt. Wenn Sie sich also für eine plattformspezifische Antwort entscheiden, sollten Sie diese zumindest deutlich als solche kennzeichnen und angeben, für welche Plattform Ihre Antwort gilt.

    – Andreas Wenzel

    5. Juni um 18:37 Uhr


1422760cookie-checkscanf() lässt das Newline-Zeichen im Puffer

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy