为什么在Python中通过字符串声明unicode?
我还在学习Python,有个疑问:
在Python 2.6.x中,我通常在文件的开头声明编码,像这样(参考PEP 0263)
# -*- coding: utf-8 -*-
之后,我的字符串就可以像平常一样写了:
a = "A normal string without declared Unicode"
但是每次我看到一个Python项目的代码时,发现文件开头并没有声明编码。相反,每个字符串都像这样声明编码:
a = u"A string with declared Unicode"
这有什么区别呢?这样做的目的是什么?我知道Python 2.6.x默认使用ASCII编码,但可以通过文件头的声明来覆盖,那么每个字符串单独声明编码又有什么意义呢?
补充:看来我把文件编码和字符串编码搞混了。谢谢你们的解释 :)
5 个回答
这段话的意思是,设置的不是字符串的格式,而是文件的格式。即使有了那个头部信息,"hello"
仍然是一个字节字符串,而不是Unicode字符串。如果你想让它变成Unicode字符串,你需要在每个地方都使用 u"hello"
。那个头部信息只是告诉你在读取 .py
文件时应该使用什么格式。
正如其他人所说,# coding:
是用来指定源文件保存时的编码方式。下面有一些例子来说明这个问题:
一个以 cp437 编码保存的文件(这是我的控制台编码),但没有声明编码
b = 'über'
u = u'über'
print b,repr(b)
print u,repr(u)
输出结果:
File "C:\ex.py", line 1
SyntaxError: Non-ASCII character '\x81' in file C:\ex.py on line 1, but no
encoding declared; see http://www.python.org/peps/pep-0263.html for details
在文件中添加了 # coding: cp437
后的输出结果:
über '\x81ber'
über u'\xfcber'
一开始,Python 不知道这个文件的编码,所以对非 ASCII 字符发出了警告。当它知道了编码后,字节字符串就得到了实际在磁盘上的字节。对于 Unicode 字符串,Python 读取了 \x81,知道在 cp437 编码中这个是一个ü,并把它解码成 Unicode 代码点,表示ü的代码是 U+00FC。当字节字符串被打印时,Python 直接把十六进制值 81
发送到控制台。当 Unicode 字符串被打印时,Python 正确地识别了我的控制台编码是 cp437,并把 Unicode ü 转换成 cp437 中的ü的值。
下面是一个以 UTF-8 编码声明和保存的文件的情况:
├╝ber '\xc3\xbcber'
über u'\xfcber'
在 UTF-8 编码中,ü 被编码为十六进制字节 C3 BC
,所以字节字符串包含这些字节,但 Unicode 字符串和第一个例子是一样的。Python 读取了这两个字节并正确解码了它们。但是,Python 打印字节字符串时出错了,因为它直接把表示ü的两个 UTF-8 字节发送到了我的 cp437 控制台。
这里的文件声明为 cp437,但实际上是以 UTF-8 保存的:
├╝ber '\xc3\xbcber'
├╝ber u'\u251c\u255dber'
字节字符串仍然得到了磁盘上的字节(UTF-8 的十六进制字节 C3 BC
),但它把这些字节解释成了两个 cp437 字符,而不是一个 UTF-8 编码的字符。这两个字符被转换成了 Unicode 代码点,结果打印出来的内容都是错误的。
这两者是不同的事情,正如其他人提到的。
当你写上 # -*- coding: utf-8 -*-
时,你是在告诉Python,你保存的源文件是 utf-8
编码。Python 2的默认编码是ASCII,而Python 3的默认编码是 utf-8
。这只是影响解释器如何读取文件中的字符。
一般来说,不管编码是什么,把高Unicode字符直接嵌入文件可能都不是个好主意;你可以使用字符串的Unicode转义,这在任何编码下都能用。
当你在字符串前加一个 u
,比如 u'This is a string'
, 这告诉Python编译器这个字符串是Unicode格式,而不是字节。解释器大部分情况下会透明地处理这个问题;最明显的区别是你现在可以在字符串中嵌入Unicode字符(也就是说, u'\u2665'
现在是合法的)。你可以使用 from __future__ import unicode_literals
来让它成为默认设置。
这只适用于Python 2;在Python 3中,默认就是Unicode,如果你想声明一个字节序列,就需要在前面加个 b
(比如 b'These are bytes'
)。