【问题标题】:binary input with an ASCII text header, read from stdin带有 ASCII 文本标头的二进制输入,从标准输入读取
【发布时间】:2015-10-07 22:02:25
【问题描述】:

我想从标准输入读取二进制PNM 图像文件。该文件包含一个编码为 ASCII 文本的标头和一个二进制的有效负载。作为阅读标题的简化示例,我创建了以下 sn-p:

#! /usr/bin/env python3
import sys
header = sys.stdin.readline()
print("header=["+header.strip()+"]")

我将它作为“test.py”运行(来自 Bash shell),在这种情况下它运行良好:

$ printf "P5 1 1 255\n\x41" |./test.py 
header=[P5 1 1 255]

但是,二进制有效负载中的一个小变化会破坏它:

$ printf "P5 1 1 255\n\x81" |./test.py 
Traceback (most recent call last):
  File "./test.py", line 3, in <module>
    header = sys.stdin.readline()
  File "/usr/lib/python3.4/codecs.py", line 313, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x81 in position 11: invalid start byte

有没有一种简单的方法可以在 Python 3 中完成这项工作?

【问题讨论】:

  • 您是否尝试更改输入编码? stackoverflow.com/a/16549381/4954037
  • @hiroprotagonist:感谢您的提示。那里指出的方法确实使我找到了一种可能的解决方案——尽管将 Unicode 解码应用于任意二进制数据有点麻烦。

标签: python-3.x unicode binary ascii


【解决方案1】:

要读取二进制数据,您应该使用二进制流,例如,使用TextIOBase.detach() method

#!/usr/bin/env python3
import sys

sys.stdin = sys.stdin.detach() # convert to binary stream
header = sys.stdin.readline().decode('ascii') # b'\n'-terminated
print(header, end='')
print(repr(sys.stdin.read()))

【讨论】:

    【解决方案2】:

    the docs,可以使用sys.stdin.buffer.read()从标准输入读取二进制数据(类型为bytes):

    要从/向标准流写入或读取二进制数据,请使用 底层二进制缓冲区对象。例如,将字节写入 标准输出,使用 sys.stdout.buffer.write(b'abc')。

    所以这是您可以采取的一个方向——以二进制模式读取数据。 readline() 和其他各种功能仍然有效。捕获 ASCII 字符串后,可以使用 decode('ASCII') 将其转换为文本,以进行额外的文本特定处理。

    或者,您可以使用io.TextIOWrapper() 来指示在输入流上使用latin-1 字符集。有了这个,隐式解码操作本质上是一个传递操作——所以数据的类型是str(代表文本),但是数据是用二进制的一对一映射来表示的(尽管每个输入字节可能使用多个存储字节)。

    以下是在任一模式下工作的代码:

    #! /usr/bin/python3
    
    import sys, io
    
    BINARY=True ## either way works
    
    if BINARY: istream = sys.stdin.buffer
    else:      istream = io.TextIOWrapper(sys.stdin.buffer,encoding='latin-1')
    
    header = istream.readline()
    if BINARY: header = header.decode('ASCII')
    print("header=["+header.strip()+"]")
    
    payload = istream.read()
    print("len="+str(len(payload)))
    for i in payload: print( i if BINARY else ord(i) )
    

    使用以下 Bash 命令测试每个可能的 1 像素有效负载:

    for i in $(seq 0 255) ; do printf "P5 1 1 255\n\x$(printf %02x $i)" |./test.py ; done
    

    【讨论】:

    • 使用latin-1 作为二进制数据的管道的破解方法有效,因为它是8-bit clean,而UTF-8 不是。
    猜你喜欢
    • 2010-12-08
    • 2011-02-20
    • 2018-04-06
    • 2010-12-01
    • 1970-01-01
    • 2015-11-23
    • 2016-12-30
    • 2013-03-30
    • 2012-02-17
    相关资源
    最近更新 更多