这通常是一个坏主意,但在您的情况下它不起作用,因为您也编码换行符。
在 UTF-16 中,每个字符都被编码为两个字节,包括您编写的换行符。因为您逐行读取文件,python 将为您提供文件中的所有数据,直到下一个换行字节,但在 UTF-16 中,这可能意味着两个字节之一仍包含在返回的数据中,导致不完整UTF-16 字节流。
要理解这一点,您需要更详细地了解 UTF-16 编码。当将 16 位数据写为 8 位的 2 个字节时,计算机需要决定先将哪个字节写入文件。这个决定可以有两种方式,称为endianess;像 Gulliver 的 Lilliputs 一样,计算机系统更喜欢 Big endian 或 Little endian 排序。
因此,UTF-16 数据流以两种顺序之一写入,并且首先写入字节顺序标记或“BOM”以标记选择了哪个。
因此,您的换行符要么被编码为'\n\x00'
or '\x00\n'
,并且在读取该空字节 ( \x00
) 时要么是您解码的 UTF-16 数据的一部分,要么是 UTF-8 数据(它被忽略的地方)。因此,如果您将 UTF-16 编码为大端序,则一切正常(但您有一个杂散的空字节),但如果您将 UTF-16 编码为小端序,事情就会中断。
基本上,编码数据应该被严格地视为二进制数据,并且您应该使用不同的方法来描绘不同的编码文本片段,或者您应该只使用将换行符严格编码为换行符的编码。
我会使用长度前缀,先读取它,然后从文件中读取每个编码数据的字节数。
>>> import struct
>>> f = open('test', 'wb')
>>> entry1 = 'this is a test\n'.encode('utf-16')
>>> struct.pack('!h', len(entry1)))
>>> f.write(entry1)
>>> entry2 = 'another test\n'.encode('utf-8')
>>> f.write(struct.pack('!h', len(entry2)))
>>> f.write(entry2)
>>> f.close()
我已经使用该struct
模块来编写定长长度的数据。请注意,我也将文件编写为二进制文件。
阅读:
>>> f = open('test', 'rb')
>>> fieldsize = struct.calcsize('!h')
>>> length = struct.unpack('!h', f.read(fieldsize))[0]
>>> print f.read(length).decode('utf-16')
this is a test
>>> length = struct.unpack('!h', f.read(fieldsize))[0]
>>> print f.read(length).decode('utf-8')
another test
>>>
该文件再次以二进制模式打开。
在现实生活中的应用程序中,您可能还必须包含每个条目的编码信息。