我正在尝试用Python读取Windows生成的UTF-16文件。据我所知,BOM是FEFF。这就是这个文件开始的地方。但是,当我将文件读入Python时,字节似乎被交换了。
(venv) [user]:~/consolidate$ head -c 16 temp.txt | od -x
0000000 feff 0022 0076 0065 0072 0073 0069 006f
0000020
(venv) [user]:~/consolidate$ python
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> with open('temp.txt', 'rb') as f:
... str = f.readline()
... print(str)
...
b'\xff\xfe"\x00v\x00e\x00r\x00s\x00i\x00o\x00n...
使用head
时,第一个字符是feff 0022
。使用Python,它看起来像是fffe2200
。这里发生了什么事?
编辑:我的问题是关于字节顺序的。以下是几点:
,
第二行读取示例:
>>> with open('temp.txt', 'rb') as f:
... str1 = f.readline()
... str2 = f.readline()
...
>>> str2
b'\x00"\x00"\x00`\x00"\x00P\x
发布于 2018-07-20 07:52:16
这里有三个不同的类似的事情。文件是一个字节序列,Python字节字符串b'\xff\xfe"\x00v\x00e\x00...'
以字节在文件中的相同顺序显示内容:
FF FE 22 00 76 00 65 00
当您运行od -x
时,它将字节对分组为16位数字。在Python系统上,2字节16位数字的标准字节排序是最低有效字节(“On byte")排在第一位,最高有效字节("256s byte")排在第二位(在x86,n=b[0]+256*b[1]
中)。所以你得到了这个小尾数的解码:
FEFF 0022 0076 0065
同时,您希望将其解码为Unicode字符。只要没有高于U+FFFF的字符,UTF16Little-endian(UTF16LE)编码就会将相同的解码转换为Unicode字符:
U+FEFF U+0022 U+0076 U+0065
<BOM> " v e
行的末尾发生了什么?让我们考虑字符串u'...",\n ...'
,并以相反的顺序完成此练习。
" , \n <SPC>
U+0022 U+002C U+000A U+0020
22 00 2C 00 0A 00 20 00
b'"\x00,\x00\n\x00 \x00'
同时:如果您没有真正考虑字符编码,而是“将其拆分到换行符上”,会发生什么?你会看到[b'"\x00,\x00"', b'\n', b'\x00 \x00']
的。这看起来像是第一部分是小端字节顺序(引号为null,逗号为null),但最后一部分是大端(空格)。但是后半部分实际上不是有效的UTF-16字符串:它包含奇数个字节,因为第一个字节实际上是换行符的后半部分。这就是当你调用readline
时发生的事情。
您有几个选项来处理此问题。在另一个答案中提到的一种方法是open(filename, 'r', encoding='utf-16')
(在文件模式中没有"b“)。然后,Python将执行正确的UTF-16解码(考虑到字节顺序标记),您将得到一个字符串。像str.readline
这样的调用在这里也会做您所期望的事情。
你还说过你的目标只是拆分文件。如果您绝对确定该文件是UTF-16LE编码的(前两个字节肯定是FF FE),那么您可以将其作为字节字符串处理(使用问题代码中的模式'rb'
),并将其拆分到您想要的UTF-16编码的字节序列上
everything = f.read()
lines = everything.split(b'\x0A\x00')
for line in lines:
parts = line.split(b'\x3A\x26')
如果您可以在一个块中读取整个文件,则更容易做到这一点;在Python中,读取10 GB的文件可能比较棘手。
发布于 2018-07-20 01:01:10
添加编码=‘utf-16’参数以打开
open('temp.txt', 'r', encoding='utf-16')
发布于 2018-07-20 01:10:15
您可以使用utf-16-le
显式地将其解码为低端字节序,然后您就会收到预期的物料清单:
>>> b'\xff\xfe"\x00v\x00e\x00r\x00s\x00i\x00o\x00n\x00'.decode('utf-16-le')
'\ufeff"version'
如果您使用utf-16
进行解码,它已经删除了物料清单:
>>> b'\xff\xfe"\x00v\x00e\x00r\x00s\x00i\x00o\x00n\x00'.decode('utf-16')
'"version'
https://stackoverflow.com/questions/51428164
复制相似问题