python - 在 python 中从 stdin 无缓冲读取

标签 python stdin unbuffered

我正在编写一个 python 脚本,它可以像这样通过管道从另一个命令读取输入

batch_job | myparser

我的脚本 myparser 处理 batch_job 的输出并写入它自己的标准输出。我的问题是我想立即看到输出(batch_job 的输出是逐行处理的)但是似乎有这个臭名昭著的标准输入缓冲(据称是 4KB,我还没有验证)延迟了一切。

问题已经讨论过了here herehere .

我尝试了以下方法:

  • 使用 os.fdopen(sys.stdin.fileno(), 'r', 0) 打开标准输入
  • 在我的 hashbang 中使用 -u:#!/usr/bin/python -u
  • 在调用脚本之前设置 export PYTHONUNBUFFERED=1
  • 在读取每一行后刷新我的输出(以防万一问题来自输出缓冲而不是输入缓冲)

我的 python 版本是 2.4.3 - 我无法升级或安装任何其他程序或软件包。我怎样才能摆脱这些延迟?

最佳答案

我在遗留代码中遇到过同样的问题。这似乎是 Python 2 的 file 对象的 __next__ 方法的实现问题;它使用 Python 级缓冲区(-u/PYTHONUNBUFFERED=1 不影响,因为那些只取消缓冲 stdio FILE * 本身,但是 file.__next__ 的缓冲不相关;同样,stdbuf/unbuffer 不能改变任何缓冲,因为 Python 替换了 C 运行时创建的默认缓冲区;file.__init__ 对新打开的文件所做的最后一件事是调用 PyFile_SetBufSize,它使用 setvbuf/setbuf [API] 来替换默认的 stdio 缓冲区)。

当您有以下形式的循环时会出现问题:

for line in sys.stdin:

__next__ 的第一次调用(由 for 循环隐式调用以获取每个 line)最终阻塞以填充之前的 block 生产单线。

存在三种可能的修复方法:

  1. (仅适用于 Python 2.6+)使用 io 模块(作为内置从 Python 3 向后移植)重新包装 sys.stdio 以绕过 file 完全支持(坦率地说是优越的)Python 3 设计(它一次使用一个系统调用来填充缓冲区,而不会阻塞整个请求的读取;如果它要求 4096 字节并得到3, 它将查看是否有一行可用并生成它)所以:

    import io
    import sys
    
    # Add buffering=0 argument if you won't always consume stdin completely, so you 
    # can't lose data in the wrapper's buffer. It'll be slower with buffering=0 though.
    with io.open(sys.stdin.fileno(), 'rb', closefd=False) as stdin:
        for line in stdin:
            # Do stuff with the line
    

    这通常比选项 2 更快,但更冗长,并且需要 Python 2.6+。它还允许重新包装是 Unicode 友好的,通过将模式更改为 'r' 并可选择传递输入的已知 encoding(如果它不是区域设置默认值)无缝获取 unicode 行而不是(仅限 ASCII)str

  2. (任何版本的 Python)通过使用 file.readline 解决 file.__next__ 的问题;尽管预期的行为几乎相同,readline 并没有做自己的(过度)缓冲,它委托(delegate)给 C stdiofgets(默认构建settings) 或手动循环调用 getc/getc_unlocked 进入缓冲区,该缓冲区在到达行尾时准确停止。通过将它与双参数 iter 结合使用,您可以获得几乎相同的代码而不会过于冗长(它可能比之前的解决方案慢,具体取决于是否使用 fgets引擎盖,以及 C 运行时如何实现它):

    # '' is the sentinel that ends the loop; readline returns '' at EOF
    for line in iter(sys.stdin.readline, ''):
        # Do stuff with line
    
  3. 移至 Python 3,它没有这个问题。 :-)

关于python - 在 python 中从 stdin 无缓冲读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33305131/

相关文章:

python - 基于递归的合并排序逻辑的替代方案

python - 从 python 文件中读取单个字符?

通过 malloc 和 getchar() 创建字符串

Python 基础 : How to read N ints until '\n' is found in stdin

c - 如何避免使用 getchar() 按下 Enter 来仅读取单个字符?

c - Linux 无缓冲从 STDIO 读取

java - 无缓冲和缓冲流

python - 如何在 Solaris 上从源代码构建 64 位 Python

python - 使用 boost::python 公开具有 std::function 作为参数的 C++ 成员函数

c - 重新分配错误 : incorrect checksum for freed object