我有这样的代码:

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个.我怎样才能使缓冲区更大?我试图提供一个更大的电话号码,但没有效果.

Update我将我的stdin阅读代码更改为:

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

    if b == "\n":
        break

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

但仍会达到这一极限.

推荐答案

这种将长行截断为4096字节的行为是由Linux内核中的终端(TTY)代码引起的.(实际上,作为截断的一部分,4096字节的最后一个字节也被换行字节替换.)当(Python)进程从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字节(包括换行字节).

有关此Linux内核行为的更多分析:

字节数限制4096在Linux内核中被硬编码为N_TTY_BUF_SIZE.

我回答的其余部分演示了如何在不截断的情况下使用Python和shell 程序(例如Bash),因此它们不会导致问题.


这是为了证明Pythonsys.stderr.readline()不会截断,因此不需要更改您的Python代码.

Python sys.stdin.readline()有一个无限的缓冲区(假设有足够的空闲内存).我已经在Linux上用Python 2.7、3.6和更新的Pythontry 过了.

以下是我try 过的:

  • 立即从管道中读取较短的行(在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']
    

    要try 它,请运行不带前导$的命令.它适用于Linux,我认为它将适用于macOS,Windows和其他系统.在Windows上,您可能需要删除if 1:和换行符.

  • 在Python3.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']
    

    要try 它,请运行不带前导$的命令.它适用于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
    

    要try 它,请运行不带前导$的命令.对我来说,它可以在Linux上运行,我认为它也可以在MacOS、Windows和其他系统上运行.在Windows上,将Python代码放到文件a.pyb.py中,然后运行python a.py | python b.py.


在某些情况下,尽可能快地(即进程一接收到输入)将输入传递给Python程序是很有用的,也就是防止在sys.stdin中缓冲导致的延迟.

好消息是:sys.stdin.readline()在进程可用时立即返回下一行,而不是等待后续行.在循环中,使用for line in iter(sys.stdin.readline, ''):,不要使用for line in sys.stdin:,因为即使有行可用,for line in sys.stdin:也会等待更多的输入.有关详细信息,请参阅https://stackoverflow.com/a/28919832/97248和其他答案.

sys.stdin.read(n)通常具有缓冲延迟:即使进程已经读取了n个字节,sys.stdin.read(n)也将等待更多字节,直到其缓冲区(通常为8192个字节)被填满.为了避免在Python3中出现这种延迟,请改用sys.stdin.buffer.raw.read(n).它将最多读取n个字节(not个Unicode字符),并且只要有至少一个字节可用,它就会立即返回.不要把它和sys.stdin.readline()混在一起.在Python2和3中,使用os.read(sys.stdin.fileno(), n)实现这一点.使用管道(例如cat | python ...)测试缓冲延迟,因为如果没有管道,系统可能会使用终端(TTY)设备,该设备在默认情况下具有线路缓冲,在行尾更早地返回数据.


这是为了证明导致截断的不是shell .以下是操作方法:

  • 在您的常规Linux图形用户界面上,运行以下任一命令(不包括前$):

    $ xterm -e dd bs=1 status=progress
    $ konsole -e dd bs=1 status=progress
    $ gnome-terminal -- dd bs=1 status=progress
    

    将出现一个新的空终端窗口.

  • 在另一个程序中,将一行长度超过4096字节的代码复制到剪贴板(或者,你可以复制一个包含多行的文本,有些长,有些短).

  • Paste it to the new, empty terminal window. If unsure, press Shift-Insert to paste. If that doesn't work, use Ctrl-Shift-V to paste. If that doesn't work either, use Edit / Paste in the menu.

  • 等一下,在窗口中按Enter.dd将显示类似... bytes (...) copied, ...的内容.字节数将小于预期,表示行截断为4096个字节.

  • 你现在可以关上windows 了.或者只需按Ctrl-C或Ctrl-D退出dd,导致窗口关闭.

这个演示已经证明,即使没有shell (或Python进程),也会发生截断.

Python相关问答推荐

如何在Python中并行化以下搜索?

如何在solve()之后获得症状上的等式的值

如何使用它?

为什么以这种方式调用pd.ExcelWriter会创建无效的文件格式或扩展名?

如何在Python脚本中附加一个Google tab(已经打开)

如何从数据库上传数据到html?

根据列值添加时区

Asyncio:如何从子进程中读取stdout?

合并与拼接并举

Pandas—MultiIndex Resample—我不想丢失其他索引的信息´

如何反转一个框架中列的值?

如何将相同组的值添加到嵌套的Pandas Maprame的倒数第二个索引级别

操作布尔值的Series时出现索引问题

设置索引值每隔17行左右更改的索引

启动线程时,Python键盘模块冻结/不工作

仅取消堆叠最后三列

将相应的值从第2列合并到第1列(Pandas )

如何在Quarto中的标题页之前创建序言页

FileNotFoundError:[WinError 2]系统找不到指定的文件:在os.listdir中查找扩展名

为什么这个正则表达式没有捕获最后一次输入?