如何在 python 3.6 中打开混合编码的 unicode 文件?
How to open mixed encoded unicode files in python 3.6?
我从未记录的资源中收到文件,其中可能包含如下所示的数据:
16058637149881541301278JA1コノマンガガスゴイヘンシュウブ4
#recordsWritten:1293462
以上只是一个示例,我正在处理的文件包含各种不同的语言(因此也包含编码)。然后我用 Python 3.6(我从 Python 2 升级到 Python 3 的继承代码库)使用以下代码打开我的文件:
import os
f = open(file_path, "r")
f.seek(0, os.SEEK_END)
f.seek(f.tell() -40, os.SEEK_SET)
records_str = f.read()
print(records_str)
使用此代码,我收到:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 0: invalid start byte
如果我将其更改为包含编码:
f = open(file_path, "r", encoding='utf-8')
,我收到同样的错误。
将编码更改为 utf-16
会导致打印:
랂菣Ꚃ菣Ɩȴ⌊敲潣摲坳楲瑴湥ㄺ㤲㐳㈶ਂ
这似乎是错误的。
将其切换为以二进制模式打开文件:f = open(file_path, "rb")
结果输出:
b'\x82\xb7\xe3\x83\xa5\xe3\x82\xa6\xe3\x83\x96\x014\x02\n#recordsWritten:1293462\x02\n'
现在稍微好一点,但是,当我最终开始处理文件时,我不想将 \x82\xb7\xe3\x83\xa5\
添加到我的数据库中,我宁愿添加 ガガスゴイヘンシ
.那么,有没有办法处理 Unicode 编码的文件呢?我还查看了 Mozilla chardet 项目以尝试确定编码,但按照代码示例,它认为文件是 utf-8 编码的。
在不知道文件中的实际字节数的情况下,我们所能做的就是推测。
如果文件自始至终都没有使用单一编码,就真的没有办法以编程方式处理它。您必须将它分成几个部分,并使用适合该序列的任何编码分别转换每个部分。这几乎肯定需要手动工作,即使只是为了在具有不同编码的部分之间建立界限。
展望未来,您可能希望将所有内容都转换为单一编码;我对此的建议是 UTF-8。它应该能够容纳任何您可以 Python 首先识别为有效字符串的内容。
作为一个粗略的例子,如果您知道您提供的示例对拉丁部分使用纯 7 位 ASCII,对日文字符使用 EUC-JP,也许可以尝试
with open(filename, 'rb') as filebytes:
raw_bytes = filebytes.read()
string = raw_bytes[0:26].decode('ascii') + \
raw_bytes[26:54].decode('euc-jp') + \
raw_bytes[54:].decode('ascii')
我根据您提供的字符串通过实验确定了字符范围;如果我猜错了你对日语文本使用的编码(特别是),它们可能对你的实际数据不正确。
观察我们如何从使用 rb
打开的文件句柄中读取 bytes
,并且 Python 在读取它们时不会尝试应用任何字符编码。但是当然,如果我们想把它变成一个字符串,我们必须 decode
它们分别使用正确的编码。
如果您 seek
进入 UTF-8 序列的中间,则错误消息并不一定意味着数据实际上不是 UTF-8,只是您无法精确搜索位置并获得有用的解码。 “无效的起始字节”意味着这不能是有效 UTF-8 字符串的开头。
如果你只需要检索文件的最后一行,也许只是读取整个文件并摘下最后一行,或者 use try
/ except
until you find a position you can safely seek to. 或者只是读取部分或全部文件作为 bytes
然后只解码最后一行。
import os
with open(file_path, "rb") as f: # notice "b" in "rb"
f.seek(0, os.SEEK_END)
f.seek(f.tell() -40, os.SEEK_SET)
records_bytes = f.read()
records_str = records_bytes.split(b'\n')[-2].decode('ascii')
print(records_str)
我们使用[-2]
假设文件末尾包含最后一个换行符(即它是一个格式正确的文本文件),因此 [-1]
只是一个空字符串,并且这将检索最后一行。
(将此作为单独的答案发布,以免污染我的其他答案,我希望这对未来的访问者也更有用。)
我从未记录的资源中收到文件,其中可能包含如下所示的数据:
16058637149881541301278JA1コノマンガガスゴイヘンシュウブ4
#recordsWritten:1293462
以上只是一个示例,我正在处理的文件包含各种不同的语言(因此也包含编码)。然后我用 Python 3.6(我从 Python 2 升级到 Python 3 的继承代码库)使用以下代码打开我的文件:
import os
f = open(file_path, "r")
f.seek(0, os.SEEK_END)
f.seek(f.tell() -40, os.SEEK_SET)
records_str = f.read()
print(records_str)
使用此代码,我收到:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x82 in position 0: invalid start byte
如果我将其更改为包含编码:
f = open(file_path, "r", encoding='utf-8')
,我收到同样的错误。
将编码更改为 utf-16
会导致打印:
랂菣Ꚃ菣Ɩȴ⌊敲潣摲坳楲瑴湥ㄺ㤲㐳㈶ਂ
这似乎是错误的。
将其切换为以二进制模式打开文件:f = open(file_path, "rb")
结果输出:
b'\x82\xb7\xe3\x83\xa5\xe3\x82\xa6\xe3\x83\x96\x014\x02\n#recordsWritten:1293462\x02\n'
现在稍微好一点,但是,当我最终开始处理文件时,我不想将 \x82\xb7\xe3\x83\xa5\
添加到我的数据库中,我宁愿添加 ガガスゴイヘンシ
.那么,有没有办法处理 Unicode 编码的文件呢?我还查看了 Mozilla chardet 项目以尝试确定编码,但按照代码示例,它认为文件是 utf-8 编码的。
在不知道文件中的实际字节数的情况下,我们所能做的就是推测。
如果文件自始至终都没有使用单一编码,就真的没有办法以编程方式处理它。您必须将它分成几个部分,并使用适合该序列的任何编码分别转换每个部分。这几乎肯定需要手动工作,即使只是为了在具有不同编码的部分之间建立界限。
展望未来,您可能希望将所有内容都转换为单一编码;我对此的建议是 UTF-8。它应该能够容纳任何您可以 Python 首先识别为有效字符串的内容。
作为一个粗略的例子,如果您知道您提供的示例对拉丁部分使用纯 7 位 ASCII,对日文字符使用 EUC-JP,也许可以尝试
with open(filename, 'rb') as filebytes:
raw_bytes = filebytes.read()
string = raw_bytes[0:26].decode('ascii') + \
raw_bytes[26:54].decode('euc-jp') + \
raw_bytes[54:].decode('ascii')
我根据您提供的字符串通过实验确定了字符范围;如果我猜错了你对日语文本使用的编码(特别是),它们可能对你的实际数据不正确。
观察我们如何从使用 rb
打开的文件句柄中读取 bytes
,并且 Python 在读取它们时不会尝试应用任何字符编码。但是当然,如果我们想把它变成一个字符串,我们必须 decode
它们分别使用正确的编码。
如果您 seek
进入 UTF-8 序列的中间,则错误消息并不一定意味着数据实际上不是 UTF-8,只是您无法精确搜索位置并获得有用的解码。 “无效的起始字节”意味着这不能是有效 UTF-8 字符串的开头。
如果你只需要检索文件的最后一行,也许只是读取整个文件并摘下最后一行,或者 use try
/ except
until you find a position you can safely seek to. 或者只是读取部分或全部文件作为 bytes
然后只解码最后一行。
import os
with open(file_path, "rb") as f: # notice "b" in "rb"
f.seek(0, os.SEEK_END)
f.seek(f.tell() -40, os.SEEK_SET)
records_bytes = f.read()
records_str = records_bytes.split(b'\n')[-2].decode('ascii')
print(records_str)
我们使用[-2]
假设文件末尾包含最后一个换行符(即它是一个格式正确的文本文件),因此 [-1]
只是一个空字符串,并且这将检索最后一行。
(将此作为单独的答案发布,以免污染我的其他答案,我希望这对未来的访问者也更有用。)