UTF-8编码错误,需要帮助转换文本
我正在为海地开发一个统计翻译系统(代码在code.google.com上),这个系统使用C++作为后台,而Python则用来驱动这个C++引擎。
我把一个UTF-8编码的Python字符串传给了C++的std::string
,经过一些处理后,又把结果返回到Python中。这里是从C++打印到Linux终端的字符串:
mwen bezwen ã ¨ d medikal
- 这是什么编码?是双重编码的字符串吗?
- 我该怎么“修复”它,让它能正常显示?
- 是因为我缺少某种字体,导致它这样打印的吗?
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 个回答
编辑:别管我之前发的那些废话了;那是错的。
正如其他人所建议的,这段代码可以让你在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
看起来确实是你的库给你返回了无用的信息,不管是输入的时候就有问题,还是其他原因。
看起来你的 默认编码 是 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 脚本,可能会产生意想不到的副作用。
看起来这是个“垃圾进,垃圾出”的问题。这里有几个提示,帮助你看看你的数据里到底有什么。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,但默默地忽略了失败 :-(