使用 Python3 处理 UTF-8 文件中的编码错误

Handling Encoding Errors in a UTF-8 File with Python3

我正在尝试使用语料库来训练 ML 模型,但我 运行 遇到了一些可能由其他人 conversion/annotation 文件引起的编码错误。在 vim 中打开文件时我可以直观地看到错误,但 python 在阅读时似乎没有注意到它们。语料库相当大,所以我需要找到一种方法 python 来检测它们,并希望有一种方法来纠正它们。

这是在 vim...

中查看的示例行

# ::snt That<92>s what we<92>re with<85>You<92>re not sittin<92> there in a back alley and sayin<92> hey what do you say, five bucks?

<92> 应该是撇号,<85> 应该是 3 个点。还有许多其他值出现在其他行上。通过谷歌搜索,我认为原始编码可能是 CP1252,但目前 Linux 下的 file 命令将此文件列为 UTF-8。我已经尝试了几种方法来打开它,但没有成功...

with open(fn) as f: returns

# ::snt Thats what were withYoure not sittin there in a back alley and sayin hey what do you say, five bucks?

跳过那些标记并连接单词,这是一个问题。

with open(fn, encoding='CP1252') as f: returns

# ::snt ThatA's what weA're withA...YouA're not sittinA' there in a back alley and sayinA' hey what do you say, five bucks?

这是在视觉上为那些奇数字符插入“A”。

with io.open(fn, errors='strict') 不会引发任何错误,读取字节流和解码也不会引发任何错误,所以不幸的是,此时我什至无法检测到更不正确的错误。

有没有办法读入这个大文件并检测其中的编码错误。更好的是,有没有办法纠正它们?

这是一个可行但不是很优雅的解决方案...

# Read in file as a raw byte-string
fn  = 'bad_chars.txt'
with open(fn, 'rb') as f:
    text = f.read()
print(text)

# Detect out of range 
has_bad = False
for c in text:
    if c >= 128:
        has_bad = True
print('Had bad:', has_bad)

# Fix offending characters
text = text.replace(b'\xc2\x92', b"\x27")
text = text.replace(b'\xc2\x85', b"...")
text = text.decode('utf-8')
print(text)

产生以下输出...

b'# ::snt That\xc2\x92s what we\xc2\x92re with\xc2\x85You\xc2\x92re not sittin\xc2\x92 there in a back alley and sayin\xc2\x92 hey what do you say, five bucks?\n'

Had bad: True

# ::snt That's what we're with...You're not sittin' there in a back alley and sayin' hey what do you say, five bucks?

缺点是我需要找到有问题的字符并编写一个 replace 命令才能工作。在类似问题中找到了 table 个可能的替换代码 efficiently replace bad characters.

使用您回答中的原始数据,您得到了来自 double-encode 的 mojibake。您需要 double-decode 才能正确翻译它。

>>> s = b'# ::snt That\xc2\x92s what we\xc2\x92re with\xc2\x85You\xc2\x92re not sittin\xc2\x92 there in a back alley and sayin\xc2\x92 hey what do you say, five bucks?\n'
>>> s.decode('utf8').encode('latin1').decode('cp1252')
'# ::snt That’s what we’re with…You’re not sittin’ there in a back alley and sayin’ hey what do you say, five bucks?\n'

数据实际上是 UTF-8,但在解码为 Unicode 时,错误的代码点是 Windows-1252 代码页的字节。 .encode('latin1') 将 Unicode 码位 1:1 转换回字节,由于 latin1 编码是 Unicode 的前 256 个码位,因此可以正确解码为 Windows- 1252.