首先,这些天我只使用 python 3.1;它的核心优点是从 unicode 对象中消除歧义的字节字符串。这使得绝大多数文本操作比以前更安全。考虑到关于 python 2.x 编码问题的数万亿个用户问题,python 2.1 的u'äbc 约定只是一个错误;有了明确的bytes 和bytearray,生活变得轻松多了。
其次,如果 py3k 不是你的口味,那么尝试使用from __future__ import unicode_literals,因为这将模仿 py3k 在 python 2.6 和 2.7 上的行为。这件事可以避免你在说 print 'exámple'.upper() 时犯的(容易犯的)错误。本质上,这与 py3k 中的相同:print( 'exámple'.encode( 'utf-8' ).upper() )。比较这些版本(对于 py3k):
print( 'exámple'.encode( 'utf-8' ).upper() )
print( 'exámple'.encode( 'utf-8' ).upper().decode( 'utf-8' ) )
print( 'exámple'.upper() )
基本上,第一个是您在使用裸字符串 'exámple' 时所做的,前提是您将默认编码设置为 utf-8(根据 BDFL 声明,在运行时设置默认编码是个坏主意,所以在 py2 中你必须通过说 import sys; reload( sys ); sys.setdefaultencoding( 'utf-8' ) 来欺骗它;我在下面为 py3k 提出了一个更好的解决方案)。当您查看这三行的输出时:
b'EX\xc3\xa1MPLE'
EXáMPLE
EXÁMPLE
您可以看到,当upper() 应用于第一个文本时,它作用于字节,而不是字符。 python 允许对字节使用upper() 方法,但它仅在字节的US-ASCII 解释上定义。由于 utf-8 使用 US-ASCII 的 内 8 位但 外 的值(128 到 255,US-ASCII 不使用),因此不会受upper() 的影响,所以当我们在第二行解码时,我们会得到小写的á。最后,第三行是正确的,是的,令人惊讶的是,python 似乎知道Á 是对应于á 的大写字母。我进行了快速测试,看看 python 3 没有在大小写之间转换哪些字符:
for cid in range( 3000 ):
my_chr = chr( cid )
if my_chr == my_chr.upper() and my_chr == my_chr.lower():
say( my_chr )
仔细阅读列表会发现很少出现拉丁文、西里尔文或希腊文字母;大部分输出是非欧洲字符和标点符号。我能发现 python 出错的唯一字符是 Ԥ/ԥ (\u0524, \u0525, '西里尔 {capital|small} letter pe with descender'),所以只要你留在拉丁扩展 X 块之外(看看那些,他们可能会产生惊喜),你可能真的会使用那个方法。当然,我没有检查映射的正确性。
最后,这是我放入 py3k 应用程序启动部分的内容:一种重新定义编码sys.stdout 的方法,使用数字字符引用 (NCR) 作为后备;这意味着打印到标准输出将永远不会引发 unicode 编码错误。当我在 ubuntu 上工作时,_sys.stdout.encoding 是 utf-8;当同一个程序在 Windows 上运行时,它可能会像 cp850 这样古怪。输出可能看起来很奇怪,但应用程序在那些愚蠢的终端上运行时不会引发异常。
#===========================================================================================================
# MAKE STDOUT BEHAVE IN A FAILSAFE MANNER
#-----------------------------------------------------------------------------------------------------------
def _harden_stdout():
"""Ensure that unprintable output to STDOUT does not cause encoding errors; use XML character references
so any kind of output gets a chance to render in a decipherable way."""
global _sys_TRM
_sys.stdout = _sys_TRM = _sys_io.TextIOWrapper(
_sys.stdout.buffer,
encoding = _sys.stdout.encoding,
errors = 'xmlcharrefreplace',
line_buffering = true )
#...........................................................................................................
_harden_stdout()
还有一条建议:在测试时,请始终尝试使用print repr( x ) 或类似的东西来揭示x 的身份。如果您只是在 py2 中 print x 并且 x 是八位字节字符串或 unicode 对象,则可能会出现各种误解。这是非常令人费解的,容易引起很多头疼。正如我所说,尝试至少从未来的导入 unicode 文字咒语中转移到 py26。
最后引用一句话:“Glyph Lefkowitz 在他的文章 Encoding 中说得最好:
我相信在这种情况下
讨论,术语“字符串”是
无意义的。有文字,有
是面向字节的数据(可能非常
很好地代表文本,但还没有
转换为它)。在 Python 类型中,
文本是 unicode。数据是str。这个想法
“非Unicode文本”只是一个
等待发生的编程错误。”
更新:刚刚发现 python 3 在大写时正确地将 ſ LATIN SMALL LETTER LONG S 转换为 S。整洁!