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提到:
        • 当流属于无缓冲时,字符将从源或者目的位置尽快输出。否则可以累积成块来传输。
        • 当流属于全缓冲时,字符将在缓冲区满时才作为块在环境中传输。
        • 当流属于行缓冲时,字符将在遇到新行时才作为块在环境中传输。
        • 此外,当缓冲区满、在无缓冲流中请求输入、在行缓冲流中请求输入时,字符将作为块在环境中传输。
        • 这些特性是已定义实现来支持的,可以被setbufsetvbuf函数影响。
    • 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
      • 但要注意这样并不能保证在新行出现时清空缓冲区,所以如果有需要,记得手动清空缓冲区。

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 have printf immediately flush every time?
      • 为什么printf不会在调用后立刻清空缓冲区,而是出现新行的时候才会?这是POSIX行为准则吗?以及我怎样使用printf才能立即清空缓冲区?
  • 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 using fprintf (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的缓冲即可:
      • setbuf(stdout, NULL);
      • or its secure version setvbuf as explained here
        • 或者更安全的setvbuf函数,具体解释见这里
      • setvbuf(stdout, NULL, _IONBF, 0);
    • 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 on stdout, to set it to unbuffered and you won’t have to worry about adding all those fflush 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 and setvbuf functions.
          • 这些特性是已定义实现来支持的,可以被setbufsetvbuf函数影响。
    • 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.
        • 但要注意这样并不能保证在新行出现时清空缓冲区,所以如果有需要,记得手动清空缓冲区。

You may also like...

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注