从 Python 中的同一文件读取二进制和文本

Reading binary and text from same file in Python

如何从 Python 中的同一个文件中读取二进制文件和文本?我知道如何分别做每一个,并且可以想象两者都非常仔细,但不是直接使用内置 IO 库。

所以我有一个文件,其格式包含大量 UTF-8 文本,其中散布着二进制数据。文本之前没有写入长度,也没有像“\0”这样的特殊字符将其从二进制数据中描绘出来,当解析意味着 "we are coming to an end".

时,靠近末尾的文本有很大一部分

最佳解决方案是让内置文件读取 类 具有 "read(n)" 和 "read_char(n)" 方法,但遗憾的是它们没有。我什至无法打开文件两次,一次是文本,一次是二进制,因为文本上 tell() 的 return 值不能以任何有意义的方式与二进制文件一起使用。

所以我的第一个想法是将整个文件作为二进制文件打开,当我看到一段文本时,阅读它 "character by character" 直到我意识到文本结束,然后返回阅读它作为二进制。然而,这意味着我必须逐字节读取并自己对 UTF-8 字符进行解码(在使用该字符之前,我是否需要读取该字符的另一个字节?)。如果它是固定宽度的字符编码,我每次只读取那么多字节。最后,我还想要 Python 文本阅读器支持的通用行结尾,但在逐字节阅读时实现起来会更加困难。

另一个更简单的解决方案是,如果我可以询问文本文件对象它在文件中的实际偏移量。仅此一项就可以解决我所有的问题。

一种方法可能是使用 Hachoir 定义文件解析协议。

简单的替代方法是以二进制模式打开文件并手动初始化缓冲区和围绕它的文本包装器。然后,您可以非常巧妙地切换进出二进制文件:

my_file = io.open("myfile.txt", "rb")
my_file_buffer = io.BufferedReader(my_file, buffer_size=1) # Not as performant but a larger buffer will "eat" into the binary data 
my_file_text_reader = io.TextIOWrapper(my_file_buffer, encoding="utf-8")
string_buffer = ""

while True:
    while "near the end" not in string_buffer:
        string_buffer += my_file_text_reader.read(1) # read one Unicode char at a time

    # binary data must be next. Where do we get the binary length from?
    print string_buffer
    data = my_file_buffer.read(3)

    print data
    string_buffer = ""

一种更快、不太可扩展的方法可能是使用您在问题中建议的方法,通过智能解析文本部分,一次读取每个 UTF-8 字节序列。以下代码(来自 http://rosettacode.org/wiki/Read_a_file_character_by_character/UTF8#Python)似乎是一种将 UTF-8 字节保守地读入二进制文件中的字符的巧妙方法:

 def get_next_character(f):
     # note: assumes valid utf-8
     c = f.read(1)
     while c:
         while True:
             try:
                 yield c.decode('utf-8')
             except UnicodeDecodeError:
                 # we've encountered a multibyte character
                 # read another byte and try again
                 c += f.read(1)
             else:
                 # c was a valid char, and was yielded, continue
                 c = f.read(1)
                 break

# Usage:
with open("input.txt","rb") as f:
    my_unicode_str = ""
    for c in get_next_character(f):
        my_unicode_str += c