如何解决Python中希腊字符解码和打印的难题?
我正在制作一个简单的游戏,目的是让用户输入一个英文单词的希腊语翻译。例如:
cow: # here, the gamer would answer with *η αγελάδα* in order to score one point.
我使用一个辅助函数来读取和解码一个文本文件。我在这个函数中使用了以下代码:
# The variable filename refers to my helper function's sole parameter, it takes the
# above mentioned txt file as an argument.
words_text = codecs.open(filename, 'r', 'utf-8')
这个辅助函数会逐行读取文件。每一行的内容大概是这样的:
# In stack data, when I debug, it reads as u"\η αγελάδα - cow\r\n".
u"\u03b7 \u03b1\u03b3\u03b5\u03bb\u03ac\u03b4\u03b1 - cow\r\n"
不过,文件的第一行在读取时有一个多余的前缀,叫做ueff-:
# u"\ufeffη αγελάδα - cow\r\n"
u"\ufeff\u03b7 \u03b1\u03b3\u03b5\u03bb\u03ac\u03b4\u03b1 - cow\r\n"
注意:在查看了Mark的回答后,我发现这个前缀(ueff)是一个BOM签名(它用来区分UTF-8和其他编码)。
这只是个小问题,我不太确定怎么才能最干净地去掉它。无论如何,我的辅助函数会创建并返回一个新的字典,内容大概是这样的:
{u'\u03b7 \u03b1\u03b3\u03b5\u03bb\u03ac\u03b4\u03b1': 'cow'}
然后,在我的主函数中,我使用以下代码来存储用户的输入:
# This is the code for the prompt I noted at the beginning.
# The variable gr_en_dict is the dictionary noted right above.
for key in gr_en_dict:
user_reply = raw_input('%s: ' % (gr_en_dict[key])).decode(sys.stdout.encoding)
接着,我将用户输入的值与字典中相应的键进行比较:
# I imported unicodedata as ud.
if ud.normalize('NFC', user_reply) == ud.normalize('NFC', key):
score += 1
在一个与我类似的问题的回答中,用户ΤΖΩΤΖΙΟΥ建议我导入unicodedata模块,并调用normalize方法(我在上面的代码中已经这么做了),但我怀疑这可能不是必要的。不幸的是,程序的这个步骤现在并不重要,因为我在解码用户输入时遇到了问题。为了演示,当我打印用户回复的标准字符串表示和字典中相应键的表示时(使用内置的repr()),我得到了以下结果:
用户的输入(user_reply):
u'? \u03b1?\u03b5??\u03b4\u03b1'
如果我不使用repr()函数打印用户的输入,它看起来是这样的:
? α?ε??δα
字典中的键:
u'\u03b7 \u03b1\u03b3\u03b5\u03bb\u03ac\u03b4\u03b1'
如果我不使用repr()打印它,我会得到一个错误:
UnicodeEncodeError: 'charmap' codec can't encode character u'\u03b7' in position 0: character maps to <undefined>
注意用户输入中的问号,以及当我尝试正确打印希腊单词时出现的错误。这似乎是我问题的关键。
那么,我到底需要做什么才能解码用户的输入,并正确显示所有希腊字符呢?
使用我的本地代码页时:
C:\>chcp
Active code page: 437
C:\>\python25\python
Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.stdout.encoding
'cp437'
>>> print '? α?ε??δα'
? α?ε??δα
>>>
使用希腊代码页时:(奇怪的是,只有在先复制到剪贴板,然后再粘贴到文字处理应用程序中时,它才会正确显示。我本来想发一张在默认控制台中实际打印的图像,但我没有足够的声望来做到这一点。)
C:\>chcp 869
Active code page: 869
C:\>\python25\python
Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.stdout.encoding
'cp869'
>>> print ' η αγελάδα'
η αγελάδα
>>> print 'η αγελάδα'
η αγελάδα
>>>
补充:我不得不将默认控制台的字体更改为Lucida Console。这解决了我的不一致问题。
2 个回答
标准的Windows命令行在处理一些特殊字符时会有问题。我建议你可以使用Windows PowerShell,这样会更好。
对于'\ufeff'这个字符,它是一个字节顺序标记。你可以在读取文件后进行以下检查:
words_text = codecs.open(filename, 'r', 'utf-8')
words_text_lines = words_text.readlines()
if words_text_lines and words_text_lines[0][0]==unicode(codecs.BOM_UTF8, 'utf8'):
words_text_lines[0] = words_text_lines[0][1:]
这样的话,如果这个字符存在,就可以把它去掉。
关于你问题的部分,可以使用:
words_text = codecs.open(filename, 'r', 'utf-8-sig')
这样可以处理字节顺序标记(byte-order-mark),也就是 \ufeff。
从技术上讲,这个:
user_reply = raw_input('%s: ' % (gr_en_dict[key])).decode(sys.stdout.encoding)
应该是:
user_reply = raw_input('%s: ' % (gr_en_dict[key])).decode(sys.stdin.encoding)
但实际上,它们应该是相同的编码。
我认为问题出在你默认控制台的编码不支持所有希腊字符。当我切换到希腊编码页面时,情况就好多了。注意,我可以把正确的字符粘贴到下面的 print
语句中,但 cp437 实际上并不支持所有字符,所以打印出来时不支持的字符会被问号替代:
C:\>chcp
Active code page: 437
C:\>python
Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.stdout.encoding
'cp437'
>>> print 'η αγελάδα - cow'
? α?ε??δα - cow
如果我切换到希腊编码页面(869 或 1253),它就能正常工作:
C:\>chcp 869
Active code page: 869
C:\>python
Python 2.7.1 (r271:86832, Nov 27 2010, 18:30:46) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.stdout.encoding
'cp869'
>>> print 'η αγελάδα - cow'
η αγελάδα - cow
>>>