除非换行符在格式字符串中,为什么printf在调用后不会刷新?

Cra*_*enz 509 c printf flush

printf除非换行符在格式字符串中,为什么在调用后不刷新?这是POSIX的行为吗?我怎么可能printf每次都立即冲洗?

Rud*_*ski 672

stdout流缓冲,所以将只显示什么是在缓冲区到达后换行(或者当它告知).您可以选择立即打印:

使用stderr以下方式打印到stderr :

fprintf(stderr, "I will be printed immediately");
Run Code Online (Sandbox Code Playgroud)

无论何时需要使用,都要刷新标准输出fprintf:

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer
Run Code Online (Sandbox Code Playgroud)

编辑:从下面的Andy Ross的评论中,您还可以使用stderr以下命令禁用stdout上的缓冲:

setbuf(stdout, NULL);
Run Code Online (Sandbox Code Playgroud)

  • 或者,完全禁用缓冲:`setbuf(stdout,NULL);` (255认同)
  • 另外,只是想提一下,显然在UNIX中,如果stdout是终端,换行通常只会刷新缓冲区.如果输出被重定向到文件,则换行不会刷新. (76认同)
  • "最初打开时,标准错误流未完全缓冲;标准输入和标准输出流完全缓冲,当且仅当流可以确定不参考交互设备时" - 请参阅此问题:http:/ /stackoverflow.com/questions/5229096/does-printf-always-flush-the-buffer-on-encountering-a-newline (7认同)
  • 我觉得我应该补充一下:我刚刚测试了这个理论,我发现在一个没有定向到终端*的流上使用`setlinebuf()`在每一行的末尾都是*刷新. (4认同)
  • @RuddZwolinski如果这将是“为什么不打印”的一个很好的标准答案,则根据[[printf是否总是在遇到换行符时总是刷新缓冲区? ://stackoverflow.com/questions/5229096/does-printf-always-flush-the-buffer-on-encountering-a-newline)直接与需要阅读评论的人对比... (2认同)

pax*_*blo 122

不,它不是POSIX行为,它是ISO行为(嗯,它 POSIX行为,但只在它们符合ISO的范围内).

如果可以检测到标准输出是指参考交互式设备,则标准输出是行缓冲的,否则它是完全缓冲的.所以有些情况下printf不会刷新,即使它有一个换行符,例如:

myprog >myfile.txt
Run Code Online (Sandbox Code Playgroud)

这对效率很有意义,因为如果您与用户进行交互,他们可能希望看到每一行.如果您将输出发送到文件,则很可能是另一端没有用户(尽管不是不可能,但他们可能正在拖尾文件).现在你可以争辩说用户想要看到每个角色但是有两个问题.

首先是它效率不高.第二个原因是ANSI C的原始授权主要是对现有行为进行编码,而不是发明行为,而这些设计决策是在ANSI开始流程之前做出的.在改变标准中的现有规则时,即便是ISO现在也非常谨慎.

至于如何处理,如果fflush (stdout)你想要立即看到每个输出调用后,这将解决问题.

或者,您可以setvbuf在操作之前使用stdout,将其设置为无缓冲,您不必担心将所有这些fflush行添加到您的代码中:

setvbuf (stdout, NULL, _IONBF, BUFSIZ);
Run Code Online (Sandbox Code Playgroud)

只要记住这可能会影响性能相当多,如果你正在发送输出到文件.另请注意,对此的支持是实现定义的,不受标准保证.

ISO C99部分7.19.3/3是相关位:

当流未缓冲时,字符应尽快从源或目的地出现.否则,可以将字符作为块累积并发送到主机环境或从主机环境发送.

当流被完全缓冲时,当填充缓冲区时,字符将作为块传输到主机环境或从主机环境传输.

当流被行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输.

此外,当填充缓冲区,在无缓冲流上请求输入时,或者在需要从主机环境传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境. .

对这些特性的支持是实现定义的,并且可能通过setbufsetvbuf函数受到影响.

  • @QiangXu,标准输出仅在可以确定为引用交互设备的情况下进行行缓冲.因此,例如,如果使用`myprog>/tmp/tmpfile`重定向输出,则完全缓冲而不是行缓冲.从内存中,确定您的标准输出是否是交互式的是由实现决定的. (10认同)
  • 我刚刚遇到一个场景,即使有'\n',printf()也不会刷新.正如你在这里提到的那样,通过添加fflush(stdout)来克服它.但我想知道为什么'\n'无法刷新printf()中的缓冲区. (7认同)
  • 此外在Windows上调用setvbuf(....,_ IOLBF)将无法正常工作,因为_IOLBF与_IOFBF相同:https://msdn.microsoft.com/en-us/library/86cebhfs.aspx (2认同)

Sou*_*ity 28

这可能是因为效率,因为如果你有多个程序写入单个TTY,这样你就不会在一行上交换字符.因此,如果程序A和B正在输出,您通常会得到:

program A output
program B output
program B output
program A output
program B output
Run Code Online (Sandbox Code Playgroud)

这很糟糕,但它比

proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output
Run Code Online (Sandbox Code Playgroud)

请注意,它甚至不能保证在换行符上刷新,因此如果刷新对您很重要,则应明确刷新.


Aar*_*ron 24

要立即刷新呼叫fflush(stdout)fflush(NULL)(NULL意味着冲洗所有内容).

  • 请记住`fflush(NULL);`通常是一个非常糟糕的主意.如果您打开了许多文件,它将会破坏性能,尤其是在多线程环境中,您将与锁定所有内容作斗争. (27认同)

小智 15

注意:Microsoft运行时库不支持行缓冲,因此printf("will print immediatelly to terminal"):

http://msdn.microsoft.com/en-us/library/86cebhfs.aspx

  • 比"printf"在"正常"情况下立即进入终端更糟糕的是,即使在立即使用其输出的情况下,`printf`和`fprintf`也会得到更粗略的缓冲.除非MS有固定的东西,否则一个程序无法从另一个程序中捕获stderr和stdout,并确定每个程序发送的内容. (2认同)

Dou*_*der 11

stdout是缓冲的,因此只会在打印换行符后输出.

要获得即时输出,请:

  1. 打印到stderr.
  2. 使stdout无缓冲.

  • 或者``fflush(stdout)`. (9认同)
  • "因此只会在打印换行符后输出." 不仅如此,还有至少4个其他案例.缓冲区已满,写入`stderr`(后面提到这个答案),`fflush(stdout)`,`fflush(NULL)`. (2认同)

小智 11

默认情况下,stdout是行缓冲的,stderr是无缓冲的,文件是完全缓冲的.


Ras*_*Kaj 10

你可以fprintf到stderr,这是无缓冲的.或者你可以在你想要的时候刷新标准输出.或者您可以将stdout设置为unbuffered.


o_O*_*o_O 10

通常有 2 个级别的缓冲 -

1. 内核缓冲区Cache(使读/写更快)

2. I/O 库中的缓冲(减少系统调用次数)

让我们举个例子fprintf and write()

当您调用 时fprintf(),它不会直接写入文件。它首先进入程序内存中的 stdio 缓冲区。从那里,它通过使用 write 系统调用写入内核缓冲区高速缓存。因此,跳过 I/O 缓冲区的一种方法是直接使用 write()。其他方法是使用setbuff(stream,NULL). 这将缓冲模式设置为无缓冲,数据直接写入内核缓冲区。为了强制将数据转移到内核缓冲区,我们可以使用“\n”,在默认缓冲模式“行缓冲”的情况下,它将刷新I/O缓冲区。或者我们可以使用fflush(FILE *stream).

现在我们位于内核缓冲区中。内核(/OS)希望最大限度地减少磁盘访问时间,因此它只读取/写入磁盘块。因此,当发出 a 时read()(这是一个系统调用,可以直接调用或通过 调用fscanf()),内核从磁盘读取磁盘块并将其存储在缓冲区中。之后,数据从此处复制到用户空间。

类似地,fprintf()从 I/O 缓冲区接收的数据由内核写入磁盘。这使得 read() write() 更快。

现在要强制内核启动一个write(),然后由硬件控制器控制数据传输,还有一些方法。我们可以O_SYNC在写调用期间使用 或类似的标志。或者我们可以使用其他函数,例如fsync(),fdatasync(),sync()让内核在内核缓冲区中有数据可用时立即启动写入操作。


小智 9

使用setbuf(stdout, NULL);禁用缓冲.