scanf () belässt das neue Zeilenzeichen im Puffer

86

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() ein neues Zeilenzeichen im Puffer belassen wurde. Daher stoppt das Programm nicht in Zeile 4, damit der Benutzer die Daten eingeben kann, sondern speichert das neue Zeilenzeichen in c2 und wechselt zu Zeile 5.

Ist das richtig?

Kommt dies jedoch nur bei charDatentypen vor? Weil ich dieses Problem bei intDatentypen wie in Zeile 1, 2, 3 nicht gesehen habe. Ist es richtig?

ipkiss
quelle
Es wird manchmal vorgeschlagen, dass fflush(stdin)vor dem Aufruf scanf()für ein einzelnes Zeichen verwendet werden kann. 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
Könnten Sie uns bitte mitteilen, auf welches Buch Sie sich beziehen?
Surya Kiran

Antworten:

76

Die scanf()Funktion überspringt führende Leerzeichen automatisch, bevor versucht wird, andere Konvertierungen als Zeichen zu analysieren. Die Zeichenformate (hauptsächlich %c; auch Scan-Sets %[…]- und%n ) sind die Ausnahme; Sie überspringen keine Leerzeichen.

Mit " %c"einem führenden Leerzeichen verwenden, um optionale Leerzeichen zu überspringen. Verwenden Sie kein abschließendes Leerzeichen in einer scanf()Formatzeichenfolge.

Beachten Sie, dass dies immer noch kein nachfolgendes Leerzeichen im Eingabestream verbraucht, auch nicht bis zum Ende einer Zeile. Achten Sie also darauf, wenn Sie auch denselben Eingabestream verwenden getchar()oder verwenden fgets(). Wir veranlassen scanf nur, vor der Konvertierung Leerzeichen zu überspringen , wie dies bei %dund anderen Konvertierungen ohne Zeichen der Fall ist .


Beachten Sie, dass andere "Nicht-Leerzeichen" -Direktiven (um die POSIX-Scanf-Terminologie zu verwenden ) als Konvertierungen, wie der Literaltext in, scanf("order = %d", &order);auch keine Leerzeichen überspringen. Das Literal ordermuss mit dem nächsten zu lesenden Zeichen übereinstimmen.

Also willst du wahrscheinlich " order = %d" dort sein, wenn Sie eine neue Zeile aus der vorherigen Zeile überspringen möchten, aber dennoch eine wörtliche Übereinstimmung für eine feste Zeichenfolge wie diese Frage benötigen .

Jeremiah Willcock
quelle
7
%c, %n, %[]Sind die 3 angegebenen Erwartungen , die führenden Leerzeichen nicht verbrauchen.
chux
@chux Also löscht der Scanf in anderen Fällen alle Leerzeichen im Puffer oder ignoriert sie für die Eingabe, aber sie sind immer noch da?
Suraj Jain
@ SurajJain Ja,
chux
3
Siehe Nachgestellte leer in scanf()Format - String und scanf()fragt zweimal für die Eingabe , während ich es erwarte , nur einmal zu fragen , für eine Diskussion über folgende Leerzeichen in Formatstrings. Sie sind eine schlechte Idee - erstaunlich schlecht, wenn Sie menschliche Interaktion erwarten und schlecht für die Programminteraktion.
Jonathan Leffler
31

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

Shweta
quelle
7

Eine andere Option (die ich von hier bekommen habe ) ist das Lesen und Verwerfen der Zeilenumbruch mithilfe der Option zur Unterdrückung der Zuweisung . Dazu setzen wir einfach ein Format ein, um ein Zeichen mit einem Sternchen zwischen %und zu lesen c:

scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3

scanf liest dann das nächste Zeichen (dh die neue Zeile), weist es jedoch keinem Zeiger zu.

Am Ende würde ich jedoch die letzte Option der FAQ unterstützen :

Abhängig von Ihren Anforderungen können Sie auch scanf () / getchar () vergessen, fgets () verwenden, um eine Textzeile vom Benutzer abzurufen und selbst zu analysieren.

brandizzi
quelle
2
Das Problem bei dieser Technik besteht darin, dass, wenn der Benutzer aLeerzeichen und dann Zeilenumbruch eingibt, die unterdrückte Zeichenkonvertierung den Leerzeichen liest und dennoch den Zeilenumbruch verlässt. Wenn der Benutzer wie supercalifragilisticexpialidociouserwartet tippt a, müssen Sie mit vielen zusätzlichen Zeichen umgehen. Sie können auch nie feststellen, ob eine nachfolgende unterdrückte Konvertierung erfolgreich ist - sie werden bei der Rückgabe von nicht berücksichtigt scanf().
Jonathan Leffler
2

Verwenden Sie diese Option, getchar()bevor Sie den zweiten Anruf tätigen scanf().

scanf("%c", &c1);
getchar();  // <== remove newline
scanf("%c", &c2);
Jiwon
quelle
1
Dies funktioniert, vorausgesetzt, der Benutzer hat nichts anderes eingegeben - zum Beispiel nachgestellte Leerzeichen. Aber es ist nicht so gut wie eine Schleife, die zur nächsten neuen Zeile scannt: int c; while ((c = getchar()) != EOF && c != '\n') ;(über drei Zeilen geschrieben, wenn kein Kommentar vorhanden ist). Es ist oft ausreichend; es ist nicht narrensicher (und man muss bedenken, dass Dummköpfe sehr klug sind, wenn es darum geht, Dinge zum Absturz zu bringen).
Jonathan Leffler