如何从 stdin 读取超过 4096 字节?

问题描述 投票:0回答:1

我有这个代码:

import sys

binfile = "data.hex"
print("Paste ascii encoded data.")

line = sys.stdin.readline()
b = bytes.fromhex(line)

with open(binfile, "wb") as fp:
    fp.write(b)

问题是在

sys.stdin.readline()
调用中读取的字节数永远不会超过 4096 个字节。我怎样才能使缓冲区更大?我尝试为呼叫提供更大的号码,但这没有效果。

更新我将我的

stdin
阅读代码更改为:

line = ''
while True:
    b = sys.stdin.read(1)
    sys.stdin.flush()
    line += b

    if b == "\n":
        break

print(f"Read {len(line)} bytes")

仍然会遇到这个限制。

python stdin readline
1个回答
0
投票

这种将长行截断为 4096 字节的行为是由 Linux 内核中的 TTY 代码引起的。对于您的用例,即当将长行复制粘贴到终端窗口时,没有简单的修复方法。作为解决方法,请复制粘贴到文件(例如

infile.dat
),然后运行
python script.py <infile.dat

即使没有 Python,您也可以通过运行

dd bs=65536 of=/dev/null
来重现它,复制粘贴长度超过 4096 字节的行,然后按 Ctrl-D 来指示 EOF。输出的最后一行将以
4096 bytes (4.1 kB, 4.0 KiB) copied,
开头,表示仅读取了 4096 字节。如果您复制粘贴多行长行,您会发现每行都会被分别截断为 4096 字节(包括换行符字节)。

我的答案的其余部分演示了 Python 和 shell(例如 Bash)如何在没有截断的情况下工作,因此它们不会导致问题。


这是为了证明 Python

sys.stderr.readline()
不会截断。

Python

sys.stdin.readline()
是无限的(假设有足够的可用内存)。我已经在 Linux 上使用 Python 2.7、3.6 和更新版本的 Python 进行了尝试。

这是我尝试过的:

  • 立即从管道读取短行(Python 中没有额外的缓冲延迟):

    $ (echo -n A; sleep .3; echo a; sleep .3; echo B; sleep .3) | python -c "if 1:
      for line in iter(__import__('sys').stdin.readline, ''): print([line])"
    ['Aa\n']
    ['B\n']
    

    要尝试一下,请运行不带前导

    $
    的命令。它对我来说适用于 Linux,我认为它也适用于 macOS、Windows 和其他系统。在 Windows 上,您可能需要删除
    if 1:
    和换行符。

    仅供参考,要防止缓冲延迟,请使用

    for line in iter(sys.stdin.readline, ''):
    (如上所述)而不是
    for line in sys.stdin:
    。有关详细信息,请参阅 https://stackoverflow.com/a/28919832/97248 和其他答案。

  • 在 Python 3.x 中,使用

    sys.stdin.buffer.readline()
    :

    立即从管道中将短行读取为字节(而不是 Unicode 字符)
    $ (echo -n A; sleep .3; echo a; sleep .3; echo B; sleep .3) | python -c "if 1:
      for line in iter(__import__('sys').stdin.buffer.readline, b''): print([line])"
    [b'Aa\n']
    [b'B\n']
    

    要尝试一下,请运行不带前导

    $
    的命令。它对我来说适用于 Linux,我认为它也适用于 macOS、Windows 和其他系统。在 Windows 上,您可能需要删除
    if 1:
    和换行符。

  • 立即从管道读取长(超过 10 MiB)行:

    $ python -c "if 1:
          import sys, time; f = sys.stdout
          f.write('A' * 10987654); f.flush(); time.sleep(.3)
          f.write('aaa\n'); f.flush(); time.sleep(.3)
          f.write('B\n'); f.flush(); time.sleep(.3)" |
      python -c "if 1:
          for line in iter(__import__('sys').stdin.readline, ''): print(len(line))"
    10987658
    2
    

    要尝试一下,请运行不带前导

    $
    的命令。它对我来说适用于 Linux,我认为它也适用于 macOS、Windows 和其他系统。在 Windows 上,将 Python 代码放入文件
    a.py
    b.py
    ,然后运行
    python a.py | python b.py

  • 立即从 PTY(终端)读取长(超过 10 MiB)行:

    $ python -c "if 1:
          import sys, time; f = sys.stdout
          f.write('A' * 10987654); f.flush(); time.sleep(.3)
          f.write('aaa\n'); f.flush(); time.sleep(.3)
          f.write('B\n'); f.flush(); time.sleep(.3)" |
      python -c "if 1:
          for line in iter(__import__('sys').stdin.readline, ''): print(len(line))"
    10987658
    2
    

这证明不是 shell 导致了截断。

TODO(分):写这个。

© www.soinside.com 2019 - 2024. All rights reserved.