UTF-8编码错误,需要帮助转换文本

2 投票
3 回答
3881 浏览
提问于 2025-04-15 19:38

我正在为海地开发一个统计翻译系统(代码在code.google.com上),这个系统使用C++作为后台,而Python则用来驱动这个C++引擎。

我把一个UTF-8编码的Python字符串传给了C++的std::string,经过一些处理后,又把结果返回到Python中。这里是从C++打印到Linux终端的字符串:

mwen bezwen ã ¨ d medikal

  1. 这是什么编码?是双重编码的字符串吗?
  2. 我该怎么“修复”它,让它能正常显示?
  3. 是因为我缺少某种字体,导致它这样打印的吗?

Python的chardet库显示:

{'confidence': 0.93812499999999999, 'encoding': 'utf-8'}

但是,当我在Python中运行字符串/Unicode/解码时,给我返回的是这个错误:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 30: ordinal not in range(128)

哦,Python也会把同样的字符串打印到标准输出。

调用repr()后,打印出以下内容:' mwen bezwen \xc3\xa3 \xc2\xa8 d medikal '

3 个回答

1

编辑:别管我之前发的那些废话了;那是错的。

正如其他人所建议的,这段代码可以让你在Python中得到正确的unicode对象,假设你想要的是utf-8格式:

>>> ' mwen bezwen \xc3\xa3 \xc2\xa8 d medikal '.decode('utf-8')
u' mwen bezwen \xe3 \xa8 d medikal '
>>> print _
 mwen bezwen ã ¨ d medikal

看起来确实是你的库给你返回了无用的信息,不管是输入的时候就有问题,还是其他原因。

1

看起来你的 默认编码 是 ASCII。

你可以选择明确地转换你的 Unicode 字符串:

print u"Hellö, Wörld".encode("utf-8")

或者,如果你想在你的脚本中全局更改这个设置,可以用一个包装器替换 sys.stdout,让它以 utf-8 编码:

import sys, codecs
sys.stdout = codecs.EncodedFile(sys.stdout, "utf-8")
print u"Hellö, Wörld!"

此外,你还可以通过 sys.setdefaultencoding 永久性地更改默认编码(对整个网站有效),但这只能在 sitecustomize.py 中进行。不过,我不建议这样做——虽然看起来方便,但这会影响你系统上所有的 Python 脚本,可能会产生意想不到的副作用。

3

看起来这是个“垃圾进,垃圾出”的问题。这里有几个提示,帮助你看看你的数据里到底有什么。repr()unicodedata.name()可以帮到你。

>>> s = ' mwen bezwen \xc3\xa3 \xc2\xa8 d medikal '
>>> print repr(s.decode('utf8'))
u' mwen bezwen \xe3 \xa8 d medikal '
>>> import unicodedata
>>> unicodedata.name(u'\xe3')
'LATIN SMALL LETTER A WITH TILDE'
>>> unicodedata.name(u'\xa8')
'DIAERESIS'
>>>

更新:

如果(正如A. N. Other所暗示的)你让程序随机选择输出语言,而你怀疑它选择的是比如说韩语(a)告诉我们(b)尝试用适合那种语言的解码器来解码输出……这里不仅有韩语,还有两种中文、日文和俄文:

>>> s = ' mwen bezwen \xc3\xa3 \xc2\xa8 d medikal '
>>> for enc in 'euc-kr big5 gb2312 shift-jis euc-jp cp1251 koi8-r'.split():
    print enc, s.decode(enc)


euc-kr  mwen bezwen 찾 짢 d medikal 
big5  mwen bezwen 瓊 穡 d medikal 
gb2312  mwen bezwen 茫 篓 d medikal 
shift-jis  mwen bezwen テ」 ツィ d medikal 
euc-jp  mwen bezwen 達 即 d medikal 
cp1251  mwen bezwen ГЈ ВЁ d medikal 
koi8-r  mwen bezwen цё б╗ d medikal 
>>> 

其实都不太靠谱,尤其是koi8-r。进一步的建议:查看你正在使用的包的文档(请提供网址!)……它对编码有什么说明?你是在尝试哪两种语言之间的转换?“mwen bezwen”在你期望的输出语言中有意义吗?尝试用更大一段文本进行测试——chardet仍然显示UTF-8吗?更大的输出在你期望的语言中有意义吗?试试把英语翻译成只使用ASCII的另一种语言——你得到的ASCII输出有意义吗?你愿意分享你的Python代码和swig接口代码吗?

更新2 信息流很有趣:“一个字符串处理应用” -> “一个统计语言翻译系统” -> “一个机器翻译系统(开源/自由软件)来帮助海地(crisiscommons.org)”

请尝试用以下事实替换“未知”:

Input language: English (guess)
Output language: Haitian Creole
Operating system: linux
Python version: unknown
C++ package name: unknown
C++ package URL: unknown
C++ package output encoding: unknown

Test 1 input: unknown
Test 1 expected output: unknown
Test 1 actual output (utf8): ' mwen bezwen \xc3\xa3 \xc2\xa8 d medikal '
[Are all of those internal spaces really in the string?]

Test 2 input: 'I need medical aid.'
Test 2 expected output (utf8): 'Mwen bezwen \xc3\xa8d medikal.'
Test 2 actual output (utf8): unknown

测试2来自于谷歌翻译(alpha)微软翻译(beta):
Mwen bezwen èd medikal.
第三个词是带有重音的拉丁小写字母E(U+00E8),后面跟着'd'。

更新3

你说过“输入:utf8(也许,我觉得我的几个文件可能有不正确编码的文本)”

假设(你从未明确说明)你所有的文件都应该是UTF-8编码:

对齐的en-fr-ht语料库的压缩文件中有几个文件在尝试以UTF-8解码时崩溃。

为什么会这样的问题诊断:

chardet在这种情况下没用;它耗费了很长时间,最后猜测是ISO-8859-2(东欧,也就是Latin2),置信度在80到90%之间。

下一步:选择ht-en目录(ht使用的重音字符比fr少,因此更容易看出问题所在)。

预期:e-grave是被认为良好的ht文本中最常见的非ASCII字符(一个网站,CMU文件)……大约是第二个o-grave的三倍。第三个最常见的字符被噪音淹没了。

在文件hten.txt中统计非ASCII字节的数量。前五名:

8a 99164
95 27682
c3 8210
a8 6004
b2 2159

最后三行的解释如下:

e-grave is c3 a8 in UTF-8
o-grave is c3 b2 in UTF-8
2159 + 6004 approx == 8210
6004 approx == 3 * 2159

前两行的解释如下:

e-grave is 8a in old Western Europe DOS encodings like cp850!!
o-grave is 95 in old Western Europe DOS encodings like cp850!!
99164 approx == 3 * 27682

包含latin1或cp1252的解释不成立(8a在latin1中是控制字符;8a在cp1252中是S-caron)。

检查内容发现,这个文件是多个原始文件的混合,有些是UTF-8编码,至少有一个是cp850(或类似的)。罪魁祸首似乎是圣经!!!

编码的混合解释了为什么chardet会挣扎。

建议:

(1) 对所有输入文件实施编码检查。确保它们在一开始就转换为UTF-8,就像边境检查一样。

(2) 实施一个脚本,在发布之前检查UTF-8的可解码性。

(3) 圣经文本的拼写(乍一看)似乎与网站的拼写不同(有很多更多的撇号)。你可能想和你的克里奥尔语专家讨论一下,你的语料库是否因为不同的拼写而被扭曲……还有一个问题是单词;你期待“无酵饼”和“麻布与灰烬”会被频繁使用吗?注意cp850的内容似乎占了混合体的90%;一些圣经可能没问题,但90%似乎太多了。

(4) 为什么摩西不抱怨非UTF-8输入?可能性:(1)它在处理原始字节,也就是说,它不转换为Unicode;(2)它尝试转换为Unicode,但默默地忽略了失败 :-(

撰写回答