为什么要在python中通过字符串声明unicode?


122

我仍在学习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编码,但是它可以被标头声明覆盖,那么每个字符串声明的意义是什么?

附录:似乎我将文件编码和字符串编码混为一谈了。感谢您的解释:)


6
# coding: utf8足够好,不需要-*-
水母

1
@jellyfish我想你是想打字的# coding: utf-8
塞缪尔·哈默

应该是#coding=utf-8python.org/dev/peps/pep-0263
Shen

Answers:


167

正如其他人所提到的,这是两件事。

指定时# -*- 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在前面指定a (例如b'These are bytes',以声明字节序列)。


感谢您的解释!我将其设置为“接受”,因为它是最完整的一个:)
奥斯卡·卡巴拉

2
Python 2的默认源编码为ascii
Mark Tolonen

27
将高Unicode字符嵌入文件中实际上是一个好主意。我怀疑非英语使用者是否想在字符串中读取Unicode转义。
Mark Tolonen

@Mark:感谢您进行ASCII校正;我迅速浏览了PEP(python.org/dev/peps/pep-0263),并在序言中谈到了Latin-1。我认为在大多数情况下在文件中嵌入高Unicode字符不是一个好主意。当然,如果您在源文件中编码很多非英语字符串,可以使它更容易,但是通常这样做是为了向用户显示,因此无论如何都应该在单独的位置定义它们。单个错误配置的文本编辑器可能会破坏所有这些字符。
克里斯·B

4
如果您正在为i18nalized应用编程,则表示同意,但请考虑您是中文还是法文程序员。不只是字符串,还有注释。很好,Python可以灵活地使用源编码。Python 3甚至可以在变量名中包含非ASCII字符。
Mark Tolonen

23

就像其他人所说的,# 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代码点的那两个字符,所有内容打印不正确。


10

那没有设置字符串的格式。它设置文件的格式。即使具有该标头,它"hello"还是一个字节字符串,而不是Unicode字符串。要使其成为Unicode,您将不得不在u"hello"任何地方使用它。标头只是在读取.py文件时使用哪种格式的提示。


当时我错了,我以为他们是一样的。那么unicode字符串的用途是i18n?
奥斯卡·卡巴拉

@奥斯卡:是的,在大多数情况下。如果您使用Django之类的网站来创建网站,并且必须使用非ASCII字符来处理用户,那么这是另一种可能的用法。
icktoofay

7

标头定义是定义代码本身的编码,而不是运行时的结果字符串。

在不带utf-8标头定义的python脚本中放置诸如۲之类的非ascii字符将引发警告

错误


-1

我制作了以下名为unicoder的模块,以便能够对变量进行转换:

import sys
import os

def ustr(string):

    string = 'u"%s"'%string

    with open('_unicoder.py', 'w') as script:

        script.write('# -*- coding: utf-8 -*-\n')
        script.write('_ustr = %s'%string)

    import _unicoder
    value = _unicoder._ustr

    del _unicoder
    del sys.modules['_unicoder']

    os.system('del _unicoder.py')
    os.system('del _unicoder.pyc')

    return value

然后,您可以在程序中执行以下操作:

# -*- coding: utf-8 -*-

from unicoder import ustr

txt = 'Hello, Unicode World'
txt = ustr(txt)

print type(txt) # <type 'unicode'>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.