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

时间:2015-09-18 18:51:56

标签: python utf-8 io binaryfiles

如何在Python中读取同一文件中的二进制文本和文本?我知道如何单独完成每个操作,并且可以想象两者都非常谨慎,但不能直接使用内置的IO库。

所以我有一个文件,其格式有大块的UTF-8文本,其中散布着二进制数据。文本没有在它之前写入的长度或者像“\ 0”这样的特殊字符从二进制数据中描述它,在解析时有一大部分文本接近结尾意味着“我们即将结束”。

最佳解决方案是让内置文件读取类具有“read(n)”和“read_char(n)”方法,但是他们没有。我甚至无法打开文件两次,一次作为文本,一次作为二进制文件,因为文本中的tell()的返回值不能以任何有意义的方式与二进制文件一起使用。

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

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

1 个答案:

答案 0 :(得分:1)

一种方法可能是使用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