Stackoverflow热门问题(七)-为什么printf在遇到新行时才清空缓冲区,而不是调用后立即清空?
stackoverflow热门问题目录如有翻译问题欢迎评论指出,谢谢。
为什么printf在遇到新行时才清空缓冲区,而不是调用后立即清空?
- Crazy Chenz asked:
- 为什么
printf
不会在调用后立刻清空缓冲区,而是出现新行的时候才会?这是POSIX行为准则吗?以及我怎样使用printf才能立即清空缓冲区?
- 为什么
- Answers:
- Rudd Zwolinski - vote: 763
stdout
流默认是行缓冲,所以只会在到达新的一行时才会输出缓冲区内容(或者被告知输出的时候)。如果想立刻输出的话,这里有几个方式:- 使用
fprintf
函数的stderr
来进行打印输出(stderr默认无缓冲): fprintf(stderr, "I will be printed immediately");
- 使用
fflush
函数清空stdout
: printf("Buffered, will be flushed");fflush(stdout); // Will now print everything in the stdout buffer
- 注:Andy Ross在评论中提到的方法也可以,只要使用
setbuf
关闭stdout
的缓冲即可: setbuf(stdout, NULL);
- 或者更安全的
setvbuf
函数,具体解释见这里: setvbuf(stdout, NULL, _IONBF, 0);
- paxdiablo - vote: 134
- 不,不是POSIX行为标准,而是ISO行为标准(如果硬要说的话,它也算是POSIX行为标准,不过仅在遵照于ISO标准时)
- 如果它检测到交互式设备时,标准输出是行缓冲,否则它是全缓冲。所以这里下面提到的这个情况中,printf在碰到新行时并不会清空缓冲区:
myprog >myfile.txt
- 这可以提高不少效率,假如你和一个用户交互,他们可能想要逐行获取信息。而如果是输出成文件,那么可能就是一个无人的终端与你交互,自然不需要立刻输出(不过也可能出现有人想追踪文件进度的情况)。现在来讨论下用户想要看到每个字符,会出现的两个问题:
- 第一个问题,这样做的效率很低。第二个问题,ANSI C最初是基于现有标准来制定规则的,而不是设定新的规则,这些规则的年龄比ANSI大多了。即便是如今的ISO,在修改现有规则时,都得很小心谨慎。
- 至于解决它的办法,在输出后调用
fflush(stdout)
,就可以立即显示缓冲区内容。 - 或者也可以在
stdout
前用setvbuf
函数来设置无缓冲,这样就不用每次都在输出后面加上fflush
来清空缓冲区了。 setvbuf (stdout, NULL, _IONBF, BUFSIZ);
- 要知道如果发送文件时使用无缓冲会非常影响性能。也要直到这个是由已定义的实现来支持,而非标准。
- ISO C99标准的7.19.3/3提到:
- 当流属于无缓冲时,字符将从源或者目的位置尽快输出。否则可以累积成块来传输。
- 当流属于全缓冲时,字符将在缓冲区满时才作为块在环境中传输。
- 当流属于行缓冲时,字符将在遇到新行时才作为块在环境中传输。
- 此外,当缓冲区满、在无缓冲流中请求输入、在行缓冲流中请求输入时,字符将作为块在环境中传输。
- 这些特性是已定义实现来支持的,可以被
setbuf
和setvbuf
函数影响。
- Southern Hospitality - vote: 29
- 大概是因为效率,以及当多个程序写入一个TTY时,不会使得这些文件在行内交错。所以当A与B输出时,可以得到:
program A
output
program B output
program B output
program A output
program B output
- 虽然不太美观,但至少比这个好:
proprogrgraam m AB ououtputputt
prproogrgram amB A ououtputtput
program B output
- 但要注意这样并不能保证在新行出现时清空缓冲区,所以如果有需要,记得手动清空缓冲区。
- Rudd Zwolinski - vote: 763
Why does printf not flush after the call unless a newline is in the format string?
- Crazy Chenz asked:
- Why does
printf
not flush after the call unless a newline is in the format string? Is this POSIX behavior? How might I haveprintf
immediately flush every time?- 为什么
printf
不会在调用后立刻清空缓冲区,而是出现新行的时候才会?这是POSIX行为准则吗?以及我怎样使用printf才能立即清空缓冲区?
- 为什么
- Why does
- Answers:
- Rudd Zwolinski - vote: 763
- The
stdout
stream is line buffered by default, so will only display what's in the buffer after it reaches a newline (or when it's told to). You have a few options to print immediately:stdout
流默认是行缓冲,所以只会在到达新的一行时才会输出缓冲区内容(或者被告知输出的时候)。如果想立刻输出的话,这里有几个方式:
- Print to
stderr
instead usingfprintf
(stderr
is unbuffered by default):- 使用
fprintf
函数的stderr
来进行打印输出(stderr默认无缓冲):
- 使用
fprintf(stderr, "I will be printed immediately");
- Flush stdout whenever you need it to using
fflush
:- 使用
fflush
函数清空stdout
:
- 使用
printf("Buffered, will be flushed");fflush(stdout); // Will now print everything in the stdout buffer
- Edit: From Andy Ross's comment below, you can also disable buffering on stdout by using
setbuf
:- 注:Andy Ross在评论中提到的方法也可以,只要使用
setbuf
关闭stdout
的缓冲即可:
- 注:Andy Ross在评论中提到的方法也可以,只要使用
setbuf(stdout, NULL);
- or its secure version
setvbuf
as explained here- 或者更安全的
setvbuf
函数,具体解释见这里:
- 或者更安全的
setvbuf(stdout, NULL, _IONBF, 0);
- The
- paxdiablo - vote: 134
- No, it's not POSIX behaviour, it's ISO behaviour (well, it is POSIX behaviour but only insofar as they conform to ISO).
- 不,不是POSIX行为标准,而是ISO行为标准(如果硬要说的话,它也算是POSIX行为标准,不过仅在遵照于ISO标准时)
- Standard output is line buffered if it can be detected to refer to an interactive device, otherwise it's fully buffered. So there are situations where
printf
won't flush, even if it gets a newline to send out, such as:- 如果它检测到交互式设备时,标准输出是行缓冲,否则它是全缓冲。所以这里下面提到的这个情况中,printf在碰到新行时并不会清空缓冲区:
myprog >myfile.txt
- This makes sense for efficiency since, if you're interacting with a user, they probably want to see every line. If you're sending the output to a file, it's most likely that there's not a user at the other end (though not impossible, they could be tailing the file). Now you could argue that the user wants to see every character but there are two problems with that.
- 这可以提高不少效率,假如你和一个用户交互,他们可能想要逐行获取信息。而如果是输出成文件,那么可能就是一个无人的终端与你交互,自然不需要立刻输出(不过也可能出现有人想追踪文件进度的情况)。现在来讨论下用户想要看到每个字符,会出现的两个问题:
- The first is that it's not very efficient. The second is that the original ANSI C mandate was to primarily codify existing behaviour, rather than invent new behaviour, and those design decisions were made long before ANSI started the process. Even ISO nowadays treads very carefully when changing existing rules in the standards.
- 第一个问题,这样做的效率很低。第二个问题,ANSI C最初是基于现有标准来制定规则的,而不是设定新的规则,这些规则的年龄比ANSI大多了。即便是如今的ISO,在修改现有规则时,都得很小心谨慎。
- As to how to deal with that, if you
fflush (stdout)
after every output call that you want to see immediately, that will solve the problem.- 至于解决它的办法,在输出后调用
fflush(stdout)
,就可以立即显示缓冲区内容。
- 至于解决它的办法,在输出后调用
- Alternatively, you can use
setvbuf
before operating onstdout
, to set it to unbuffered and you won't have to worry about adding all thosefflush
lines to your code:- 或者也可以在
stdout
前用setvbuf
函数来设置无缓冲,这样就不用每次都在输出后面加上fflush
来清空缓冲区了。
- 或者也可以在
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
- Just keep in mind that may affect performance quite a bit if you are sending the output to a file. Also keep in mind that support for this is implementation-defined, not guaranteed by the standard.
- 要知道如果发送文件时使用无缓冲会非常影响性能。也要直到这个是由已定义的实现来支持,而非标准。
- ISO C99 section
7.19.3/3
is the relevant bit:- ISO C99标准的7.19.3/3提到:
- When a stream is unbuffered, characters are intended to appear from the source or at the destination as soon as possible. Otherwise characters may be accumulated and transmitted to or from the host environment as a block.
- 当流属于无缓冲时,字符将从源或者目的位置尽快输出。否则可以累积成块来传输。
- When a stream is fully buffered, characters are intended to be transmitted to or from the host environment as a block when a buffer is filled.
- 当流属于全缓冲时,字符将在缓冲区满时才作为块在环境中传输。
- When a stream is line buffered, characters are intended to be transmitted to or from the host environment as a block when a new-line character is encountered.
- 当流属于行缓冲时,字符将在遇到新行时才作为块在环境中传输。
- Furthermore, characters are intended to be transmitted as a block to the host environment when a buffer is filled, when input is requested on an unbuffered stream, or when input is requested on a line buffered stream that requires the transmission of characters from the host environment.
- 此外,当缓冲区满、在无缓冲流中请求输入、在行缓冲流中请求输入时,字符将作为块在环境中传输。
- Support for these characteristics is implementation-defined, and may be affected via the
setbuf
andsetvbuf
functions.- 这些特性是已定义实现来支持的,可以被
setbuf
和setvbuf
函数影响。
- 这些特性是已定义实现来支持的,可以被
- No, it's not POSIX behaviour, it's ISO behaviour (well, it is POSIX behaviour but only insofar as they conform to ISO).
- Southern Hospitality - vote: 29
- It's probably like that because of efficiency and because if you have multiple programs writing to a single TTY, this way you don't get characters on a line interlaced. So if program A and B are outputting, you'll usually get:
- 大概是因为效率,以及当多个程序写入一个TTY时,不会使得这些文件在行内交错。所以当A与B输出时,可以得到:
program A
output
program B output
program B output
program A output
program B output
- This stinks, but it's better than
- 虽然不太美观,但至少比这个好:
proprogrgraam m AB ououtputputt
prproogrgram amB A ououtputtput
program B output
- Note that it isn't even guaranteed to flush on a newline, so you should flush explicitly if flushing matters to you.
- 但要注意这样并不能保证在新行出现时清空缓冲区,所以如果有需要,记得手动清空缓冲区。
- It's probably like that because of efficiency and because if you have multiple programs writing to a single TTY, this way you don't get characters on a line interlaced. So if program A and B are outputting, you'll usually get:
- Rudd Zwolinski - vote: 763
共有 0 条评论