701 Stimmen

Warum führt printf nach dem Aufruf keinen Flush aus, wenn kein Zeilenumbruch in der Formatzeichenfolge enthalten ist?

Warum ist printf nach dem Aufruf nicht spülen, es sei denn, ein Zeilenumbruch ist in der Formatzeichenfolge enthalten? Ist dies ein POSIX-Verhalten? Wie könnte ich printf jedes Mal sofort spülen?

2 Stimmen

Haben Sie untersucht, ob dies mit jeder Datei oder nur mit Terminals passiert? das klingt nach einem cleveren Terminal-Feature, keine unvollendeten Zeilen aus einem Hintergrundprogramm auszugeben, obwohl ich vermute, dass es nicht für die Programm im Vordergrund.

8 Stimmen

Unter Cygwin bash sehe ich das gleiche Fehlverhalten, auch wenn ein Zeilenumbruch es in der Formatzeichenfolge. Dieses Problem ist neu in Windows 7; der gleiche Quellcode funktionierte unter Windows XP problemlos. MS cmd.exe wird wie erwartet gespült. Die Lösung setvbuf(stdout, (char*)NULL, _IONBF, 0) umgeht das Problem, wäre aber sicher nicht nötig gewesen. Ich verwende MSVC++ 2008 Express. ~~~

19 Stimmen

Um den Titel der Frage zu verdeutlichen: printf(..) führt keine Spülung durch selbst, sondern die Zwischenspeicherung von stdout die beim Erkennen eines Zeilenumbruchs gelöscht werden kann (wenn sie zeilengepuffert ist). Es würde auf die gleiche Weise reagieren auf putchar('\n'); Así que printf(..) ist in dieser Hinsicht keine Besonderheit. Dies steht im Gegensatz zu cout << endl; die deren Dokumentation wird die Spülung an prominenter Stelle erwähnt. Die Dokumentation von printf wird das Spülen überhaupt nicht erwähnt.

920voto

Rudd Zwolinski Punkte 25176

En stdout stream ist standardmäßig zeilengepuffert, zeigt also nur den Inhalt des Puffers an, nachdem er einen Zeilenumbruch erreicht hat (oder wenn er dazu aufgefordert wird). Sie haben ein paar Optionen, um sofort zu drucken:

  • Drucken auf stderr stattdessen mit fprintf ( stderr es standardmäßig ungepuffert ):

    fprintf(stderr, "I will be printed immediately");
  • Spülen stdout wann immer Sie es brauchen, indem Sie fflush :

    printf("Buffered, will be flushed");
    fflush(stdout); // Will now print everything in the stdout buffer
  • Deaktivieren Sie die Pufferung auf stdout durch die Verwendung von setbuf :

    setbuf(stdout, NULL);
  • Oder verwenden Sie die flexiblere setvbuf :

    setvbuf(stdout, NULL, _IONBF, 0);

154voto

paxdiablo Punkte 809679

Nein, es ist kein POSIX-Verhalten, es ist ein ISO-Verhalten (nun, es es POSIX-Verhalten, aber nur insoweit, als sie ISO-konform sind).

Die Standardausgabe ist zeilengepuffert, wenn erkannt werden kann, dass sie sich auf ein interaktives Gerät bezieht, ansonsten ist sie vollständig gepuffert. Es gibt also Situationen, in denen printf wird nicht gespült, selbst wenn es einen Zeilenumbruch zum Senden bekommt, wie z.B.:

myprog >myfile.txt

Dies ist aus Gründen der Effizienz sinnvoll, denn wenn Sie mit einem Benutzer interagieren, möchte dieser wahrscheinlich jede Zeile sehen. Wenn Sie die Ausgabe in eine Datei senden, ist es sehr wahrscheinlich, dass am anderen Ende kein Benutzer sitzt (obwohl es nicht unmöglich ist, er könnte die Datei verfolgen). Jetzt können Sie podría argumentieren, dass der Benutzer jedes Zeichen sehen möchte, aber es gibt zwei Probleme damit.

Der erste ist, dass sie nicht sehr effizient ist. Der zweite ist, dass der ursprüngliche Auftrag von ANSI C darin bestand, in erster Linie die bestehende Verhalten, anstatt es zu erfinden neu und diese Designentscheidungen wurden getroffen, lange bevor ANSI mit dem Prozess begann. Selbst die ISO geht heutzutage sehr vorsichtig vor, wenn sie bestehende Regeln in den Normen ändert.

Wie kann man damit umgehen, wenn man fflush (stdout) nach jedem Ausgabeaufruf, den Sie sofort sehen wollen, wird das Problem gelöst.

Alternativ können Sie auch setvbuf vor dem Einsatz am stdout auf ungepuffert einstellen, und Sie müssen sich nicht mehr um das Hinzufügen all dieser fflush Zeilen in Ihren Code einfügen:

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

Bedenken Sie jedoch, dass dies die Leistung erheblich beeinträchtigen kann, wenn Sie sont Senden der Ausgabe in eine Datei. Denken Sie auch daran, dass die Unterstützung hierfür implementierungsabhängig ist und nicht durch den Standard garantiert wird.

ISO C99 Abschnitt 7.19.3/3 ist das relevante Bit:

Wenn ein Stream ungepuffert Die Zeichen sollen so schnell wie möglich von der Quelle oder am Zielort erscheinen. Andernfalls können Zeichen akkumuliert und als Block an die oder von der Host-Umgebung übertragen werden.

Wenn ein Stream vollständig gepuffert Wenn ein Puffer gefüllt ist, sollen die Zeichen als Block zur oder von der Host-Umgebung übertragen werden.

Wenn ein Stream zeilengepuffert Wenn ein Zeilenumbruch auftritt, sollen die Zeichen als Block an die oder von der Host-Umgebung übertragen werden.

Darüber hinaus sollen Zeichen als Block an die Host-Umgebung übertragen werden, wenn ein Puffer gefüllt ist, wenn eine Eingabe auf einem ungepufferten Stream angefordert wird oder wenn eine Eingabe auf einem zeilengepufferten Stream angefordert wird, der die Übertragung von Zeichen aus der Host-Umgebung erfordert.

Die Unterstützung für diese Merkmale ist implementierungsabhängig und kann über die setbuf y setvbuf Funktionen.

38voto

Aaron Punkte 8935

Zum sofortigen Spülen rufen Sie fflush(stdout) o fflush(NULL) ( NULL bedeutet, alles zu spülen).

38voto

Wahrscheinlich ist das so, weil es effizienter ist und weil Sie, wenn Sie mehrere Programme auf einen einzigen TTY schreiben, auf diese Weise keine verschachtelten Zeichen in einer Zeile erhalten. Wenn also Programm A und B ausgeben, erhalten Sie normalerweise:

program A output
program B output
program B output
program A output
program B output

Das stinkt, aber es ist besser als

proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output

Beachten Sie, dass es nicht einmal garantiert ist, dass bei einem Zeilenumbruch geflusht wird, so dass Sie explizit flushen sollten, wenn Sie Wert auf Flushing legen.

15voto

Douglas Leeder Punkte 50423

Stdout ist gepuffert, wird also erst nach einem Zeilenumbruch ausgegeben.

Um eine sofortige Ausgabe zu erhalten, können Sie entweder:

  1. Druck auf stderr.
  2. Stdout ungepuffert machen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X