Warum wird printf nach dem Aufruf nicht geleert, es sei denn, ein Zeilenumbruch ist im Formatstring?

Lesezeit: 8 Minuten

Benutzeravatar von Crazy Chenz
Verrückter Chenz

Warum tut printf wird nach dem Aufruf nicht gelöscht, es sei denn, ein Zeilenumbruch ist im Formatstring? Ist das POSIX-Verhalten? Wie könnte ich haben printf jedes Mal sofort spülen?

  • hast du mal nachgeforscht ob das bei jeder datei passiert oder nur bei den terminals? Das klingt nach einer cleveren Terminalfunktion, um keine unvollständige Zeile aus einem Hintergrundprogramm auszugeben, obwohl ich erwarte, dass dies nicht zutreffen würde das Programm im Vordergrund.

    – PypeBros

    12. November 2009 um 16:50 Uhr

  • Unter Cygwin bash sehe ich dasselbe Fehlverhalten, auch wenn es sich um einen Zeilenumbruch handelt ist im Formatstring. Dieses Problem ist neu in Windows 7; Der gleiche Quellcode funktionierte unter Windows XP einwandfrei. MS cmd.exe wird wie erwartet geleert. Die Reparatur setvbuf(stdout, (char*)NULL, _IONBF, 0) umgeht das Problem, sollte aber sicherlich nicht notwendig gewesen sein. Ich verwende MSVC++ 2008 Express. ~~~

    – Steve Krüger

    8. Januar 2013 um 14:10 Uhr

  • Um den Titel der Frage zu verdeutlichen: printf(..) macht keine Spülung selbst, es ist die Pufferung von stdout das kann spülen, wenn ein Zeilenumbruch angezeigt wird (wenn es zeilengepuffert ist). Es würde auf die gleiche Weise reagieren putchar('\n');Also printf(..) ist diesbezüglich nicht besonders. Dies steht im Gegensatz zu cout << endl;das Dokumentation davon Erwähnt prominent das Spülen. Das Dokumentation von printf Erwähnt das Spülen überhaupt nicht.

    – Jewgeni Sergejew

    5. April 2016 um 14:02 Uhr


  • Das Schreiben (/flushing) ist möglicherweise eine teure Operation, es wird wahrscheinlich aus Leistungsgründen gepuffert.

    – Hansenrik

    17. August 2017 um 23:31 Uhr


  • @EvgeniSergeev: Gibt es einen Konsens darüber, dass die Frage das Problem falsch diagnostiziert hat und dass das Spülen auftritt, wenn ein Zeilenumbruch in der Ausgang? (Eine in die Formatzeichenfolge einzufügen ist eine Möglichkeit, aber nicht die einzige Möglichkeit, eine in die Ausgabe zu bekommen).

    – Ben Voigt

    1. Mai 2020 um 17:54 Uhr


Benutzeravatar von Rudd Zwolinski
Rudd Zwolinski

Das stdout stream ist standardmäßig zeilengepuffert, zeigt also nur an, was sich im Puffer befindet, nachdem er einen Zeilenumbruch erreicht hat (oder wenn er dazu aufgefordert wird). Sie haben einige Möglichkeiten, sofort zu drucken:

  • Drucken nach stderrstattdessen verwenden fprintf (stderr ist standardmäßig ungepuffert):

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

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

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

    setvbuf(stdout, NULL, _IONBF, 0); 
    

  • Oder um die Pufferung vollständig zu deaktivieren: setbuf(stdout, NULL);

    – Andy Roß

    11. November 2009 um 17:42 Uhr

  • Ich wollte auch nur erwähnen, dass anscheinend in UNIX ein Zeilenumbruch den Puffer normalerweise nur leert, wenn stdout ein Terminal ist. Wenn die Ausgabe in eine Datei umgeleitet wird, wird ein Zeilenumbruch nicht gelöscht.

    – hora

    5. März 2011 um 23:10 Uhr

  • Ich habe das Gefühl, dass ich hinzufügen sollte: Ich habe diese Theorie gerade getestet, und ich finde, dass sie nützlich ist setlinebuf() auf einem Stream, der nicht an ein Terminal gerichtet ist ist Spülung am Ende jeder Zeile.

    – Daddy

    6. September 2011 um 19:06 Uhr

  • “Wie ursprünglich geöffnet, ist der Standardfehlerstrom nicht vollständig gepuffert; die Standardeingabe- und Standardausgabeströme sind vollständig gepuffert, wenn und nur wenn festgestellt werden kann, dass sich der Strom nicht auf ein interaktives Gerät bezieht” – siehe diese Frage: stackoverflow.com /Fragen/5229096/…

    – Seppo Enarvi

    22. Mai 2015 um 7:23 Uhr

  • @RuddZwolinski Wenn dies eine gute kanonische Antwort auf “Warum druckt es nicht” sein soll, scheint es wichtig zu sein, die Unterscheidung zwischen Terminal und Datei zu erwähnen, wie in “Füllt printf immer den Puffer, wenn ein Zeilenumbruch auftritt?” direkt in dieser hoch bewerteten Antwort, im Gegensatz zu Leuten, die die Kommentare lesen müssen …

    – HostileFork sagt, vertraue SE nicht

    8. April 2016 um 22:08 Uhr

Benutzeravatar von paxdiablo
paxdiablo

Nein, es ist kein POSIX-Verhalten, es ist ISO-Verhalten (na ja, es ist POSIX-Verhalten, aber nur soweit sie ISO-konform sind).

Die Standardausgabe ist zeilengepuffert, wenn erkannt werden kann, dass sie sich auf ein interaktives Gerät bezieht, andernfalls ist sie vollständig gepuffert. Es gibt also Situationen, in denen printf wird nicht geleert, selbst wenn ein Zeilenumbruch gesendet wird, wie zum Beispiel:

myprog >myfile.txt

Dies ist aus Gründen der Effizienz sinnvoll, da Sie bei der Interaktion mit einem Benutzer wahrscheinlich jede Zeile sehen möchten. Wenn Sie die Ausgabe an eine Datei senden, ist höchstwahrscheinlich kein Benutzer am anderen Ende (obwohl nicht unmöglich, er könnte die Datei verfolgen). Jetzt du könnte argumentieren, dass der Benutzer jedes Zeichen sehen möchte, aber damit gibt es zwei Probleme.

Das erste ist, dass es nicht sehr effizient ist. Das zweite ist, dass das ursprüngliche ANSI C-Mandat hauptsächlich darin bestand, zu kodifizieren vorhandenen Verhalten statt erfinden Neu Verhalten, und diese Entwurfsentscheidungen wurden getroffen, lange bevor ANSI mit dem Prozess begann. Auch ISO geht heutzutage sehr vorsichtig vor, wenn es darum geht, bestehende Regeln in den Standards zu ändern.

Wie man damit umgeht, ggf fflush (stdout) Nach jedem Ausgabeaufruf, den Sie sofort sehen möchten, wird das Problem behoben.

Alternativ können Sie verwenden setvbuf vor dem operieren stdoutum es auf ungepuffert zu setzen, und Sie müssen sich keine Gedanken darüber machen, all diese hinzuzufügen fflush Zeilen zu deinem Code:

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

Denken Sie nur daran, dass dies die Leistung erheblich beeinträchtigen kann, wenn Sie sind Senden der Ausgabe an eine Datei. Denken Sie auch daran, dass die Unterstützung hierfür von der Implementierung definiert und nicht vom Standard garantiert wird.

Abschnitt ISO C99 7.19.3/3 ist das relevante Bit:

Wenn ein Stream ist ungepuffert, Zeichen sollen so schnell wie möglich von der Quelle oder am Ziel erscheinen. Andernfalls können Zeichen akkumuliert und als Block zu oder von der Hostumgebung übertragen werden.

Wenn ein Stream ist vollständig gepuffertsollen Zeichen als Block an die oder von der Hostumgebung übertragen werden, wenn ein Puffer gefüllt ist.

Wenn ein Stream ist Leitung gepuffertsollen Zeichen als Block an die oder von der Hostumgebung übertragen werden, wenn ein Zeilenumbruchzeichen gefunden wird.

Außerdem sollen Zeichen als Block an die Hostumgebung übertragen werden, wenn ein Puffer gefüllt ist, wenn eine Eingabe auf einem ungepufferten Strom angefordert wird oder wenn eine Eingabe auf einem zeilengepufferten Strom angefordert wird, der die Übertragung von Zeichen aus der Hostumgebung erfordert .

Die Unterstützung für diese Merkmale ist implementierungsdefiniert und kann über die beeinflusst werden setbuf und setvbuf Funktionen.

  • Ich bin gerade auf ein Szenario gestoßen, in dem selbst dort ein ‘\n’ vorhanden ist, printf() nicht spült. Es wurde überwunden, indem ein fflush (stdout) hinzugefügt wurde, wie Sie hier erwähnt haben. Aber ich frage mich, warum ‘\n’ den Puffer in printf() nicht leeren konnte.

    – Qiang Xu

    28. April 2012 um 19:45 Uhr

  • @QiangXu, die Standardausgabe wird nur dann zeilengepuffert, wenn eindeutig festgestellt werden kann, dass sie sich auf ein interaktives Gerät bezieht. Wenn Sie also beispielsweise die Ausgabe mit umleiten myprog >/tmp/tmpfile, die vollständig gepuffert und nicht zeilengepuffert ist. Aus dem Gedächtnis heraus bleibt die Entscheidung, ob Ihre Standardausgabe interaktiv ist, der Implementierung überlassen.

    – paxdiablo

    29. April 2012 um 0:20 Uhr


  • außerdem funktioniert der Aufruf von setvbuf(…., _IOLBF) unter Windows nicht, da _IOLBF dort dasselbe wie _IOLBF ist: msdn.microsoft.com/en-us/library/86cebhfs.aspx

    – Piotr Lopusiewicz

    5. Februar 2015 um 10:02 Uhr

Dies ist wahrscheinlich aus Gründen der Effizienz so und weil Sie, wenn Sie mehrere Programme in ein einziges TTY schreiben, auf diese Weise keine Zeichen in einer Zeile verschachtelt 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, bei einem Zeilenumbruch zu leeren, also sollten Sie explizit leeren, wenn das Leeren für Sie wichtig ist.

  • Komisch, ich denke, das ist die einzige Antwort, die das “Warum?” wirklich beantwortet. – Anwenden einer angemessenen Menge an Raten. Die anderen erklären das es wird gepuffert (was für ein OP weniger notwendig erscheint, das sich dessen bewusst ist, indem es den Begriff “Flushing” verwendet) und wie es vermieden / kontrolliert werden kann. Zugegebenermaßen gibt es dort genug Details, um hilfreiche Einblicke in Antworten zu geben. Aber nur dieser diskutiert warum und hat diesen Winkel der Beantwortung ganz für sich. Prost.

    – Yunnosch

    5. Februar um 7:58

Benutzeravatar von Aaron
Aaron

Um sofort zu spülen, rufen Sie an fflush(stdout) oder fflush(NULL) (NULL bedeutet alles spülen).

stdout ist gepuffert, wird also nur ausgegeben, nachdem ein Zeilenumbruch gedruckt wurde.

Um eine sofortige Ausgabe zu erhalten, entweder:

  1. Druck auf stderr.
  2. Machen Sie stdout ungepuffert.

  • Oder fflush(stdout).

    – RastaJedi

    22. Februar 2016 um 22:47 Uhr

  • “wird also erst ausgegeben, nachdem ein Zeilenumbruch gedruckt wurde.” Nicht nur das, sondern mindestens 4 weitere Fälle. Puffer voll, schreiben stderr (diese Antwort wird später erwähnt), fflush(stdout), fflush(NULL).

    – chux – Wiedereinsetzung von Monica

    22. Dezember 2017 um 10:20 Uhr


  • “stdout ist gepuffert” ist nicht wahr, wie in Aufzählungspunkt 2 angedeutet. Standardmäßig wird stdout blockgepuffert, wenn es sich um eine normale Datei handelt, und zeilengepuffert, wenn es sich um ein tty handelt. Fügen Sie vielleicht einfach “standardmäßig” zum Ausdruck “stdout is buffered” hinzu.

    – William Pursel

    14. Dezember 2021 um 15:14 Uhr

Benutzeravatar von phuclv
phuclv

Hinweis: Microsoft-Laufzeitbibliotheken unterstützen keine Zeilenpufferung, daher printf("will print immediately to terminal"):

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf

  • Oder fflush(stdout).

    – RastaJedi

    22. Februar 2016 um 22:47 Uhr

  • “wird also erst ausgegeben, nachdem ein Zeilenumbruch gedruckt wurde.” Nicht nur das, sondern mindestens 4 weitere Fälle. Puffer voll, schreiben stderr (diese Antwort wird später erwähnt), fflush(stdout), fflush(NULL).

    – chux – Wiedereinsetzung von Monica

    22. Dezember 2017 um 10:20 Uhr


  • “stdout ist gepuffert” ist nicht wahr, wie in Aufzählungspunkt 2 angedeutet. Standardmäßig wird stdout blockgepuffert, wenn es sich um eine normale Datei handelt, und zeilengepuffert, wenn es sich um ein tty handelt. Fügen Sie vielleicht einfach “standardmäßig” zum Ausdruck “stdout is buffered” hinzu.

    – William Pursel

    14. Dezember 2021 um 15:14 Uhr

Benutzer-Avatar von woso
weh

Standardmäßig ist stdout zeilengepuffert, stderr ist nicht gepuffert und file ist vollständig gepuffert.

1428270cookie-checkWarum wird printf nach dem Aufruf nicht geleert, es sei denn, ein Zeilenumbruch ist im Formatstring?

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

Privacy policy