Python2.7中的编码

2024-03-29 13:30:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我对Python2.7中的编码有一些疑问。

1.python代码如下

#s = u"严"
s = u'\u4e25'
print 's is:', s
print 'len of s is:', len(s)
s1 = "a" + s
print 's1 is:', s1
print 'len of s1 is:', len(s1)

输出为:

s is: 严
len of s is: 1
s1 is: a严
len of s1 is: 2

我很困惑,为什么s的len是1,如何将4e25存储在1字节中?我还注意到USC-2是2字节长,USC-4是4字节长,为什么unicode字符串s的长度是1?

2。 (1) 用notepad++(Windows 7)新建一个名为a.py的文件,并设置该文件的编码ANSI,在a.py中的代码如下:

# -*- encoding:utf-8 -*-
import sys
print sys.getdefaultencoding()
s = "严"
print "s:", s
print "type of s:", type(s)

输出为:

ascii
s: 严
type of s: <type 'str'>

(2)使用记事本++(Windows7)新建一个名为b.py的文件,并设置该文件的编码UTF-8,在b.py中的代码如下:

# -*- encoding:gbk -*-
import sys
print sys.getdefaultencoding()
s = "严"
print "s:", s
print "type of s:", type(s)

输出为:

  File "D:\pyws\code\\b.py", line 1
SyntaxError: encoding problem: utf-8

(3)将文件b.py更改如下(文件的编码方式为UTF-8):

import sys
print sys.getdefaultencoding()
s = "严"
print "s:", s
print "type of s:", type(s)

输出为:

ascii
s: 涓
type of s: <type 'str'>

(4)将文件a.py更改如下(文件的编码方式为ANSI):

import sys
print sys.getdefaultencoding()
s = "严"
print "s:", s
print "type of s:", type(s)

输出为:

  File "D:\pyws\code\a1.py", line 3
SyntaxError: Non-ASCII character '\xd1' in file D:\pyws\code\a1.py on
line 3, but no encoding declared; see http://www.python.org/peps/pep-0263.html f
or details

为什么问题2中这4个案例的输出不同?有人能详细了解吗?


Tags: 文件of代码pyimport编码len字节
2条回答

I am confused that why the len of s is 1, how could 4e25 be stored in 1 byte? I also notice that USC-2 is 2-bytes long and USC-4 is 4-bytes long, why unicode string s's length is 1?

unicode字符串的全部目的就是这样做。unicode字符串的长度是字符数(即,代码点),而不是字节数。字节数可能因编码而异,但字符数是一个抽象的不变量,不会随编码而改变。

至于第二个问题,答案是在设置文件的编码时,告诉Python如何将该文件中的字节映射到字符。如果指定的编码(使用# encoding语法)与文件的实际编码不一致,则会出现不可预测的行为,因为Python试图以一种方式解释字节,但文件的设置使字节实际上意味着其他东西。

您得到的行为类型将取决于您使用的编码的具体情况。一些可能性是:

  1. 你会很幸运,即使你使用了相互冲突的编码,它也会起作用;这是在你的第一个案例中发生的。
  2. 它将引发一个错误,因为文件中的字节与指定的编码不一致;这是在第二种情况下发生的情况。
  3. 它似乎可以工作,但会产生不同的字符,因为文件实际编码中的字节在用指定编码解释时意味着其他一些东西。这似乎是在您的第三种情况下发生的,尽管它应该会引发一个错误,因为该字符不是ASCII。(所谓“文件的编码样式是UTF-8”是指在文件中设置了一个# encoding指令吗?)
  4. 如果不指定任何编码,如果尝试使用非纯ASCII格式的任何字节,则会出现错误。这就是你上次的案子。

而且,在所有情况下,字符串的类型都是str,因为您没有将字符串指定为unicode(例如,使用u"...")。指定文件编码不会使字符串成为unicode。它只是告诉Python如何解释文件中的字符。

然而,这里还有一个更大的问题,那就是:你为什么要用你的例子中的编码来玩那些游戏?没有任何理由使用# encoding标记来指定文件实际编码之外的编码,这样做肯定会导致问题。别这么做。您必须知道文件的编码方式,并在# encoding标记中指定相同的编码方式。

对问题1的回答:

在Python版本<;3.3中,Unicode字符串u''的长度是使用的UTF-16或UTF-32代码单元数(取决于生成标志),而不是字节数。\u4e25是一个代码单元,但如果使用UTF-16(Windows上的默认值),则并非所有字符都由一个代码单元表示。

>>> len(u'\u42e5')
1
>>> len(u'\U00010123')
2

在Python3.3中,上述两个函数都将返回1。

Unicode字符也可以由组合代码单元组成,例如énormalize函数可用于生成组合形式或分解形式:

>>> import unicodedata as ud
>>> ud.name(u'\xe9')
'LATIN SMALL LETTER E WITH ACUTE'
>>> ud.normalize('NFD',u'\xe9')
u'e\u0301'
>>> ud.normalize('NFC',u'e\u0301')
u'\xe9'

因此,即使在Python3.3中,一个显示字符也可以有一个或多个代码单元,为了得到一致的答案,最好将其规范化为一种或另一种形式。

问题2的答案:

文件顶部声明的编码必须与保存文件的编码一致。声明让Python知道如何解释文件中的字节。

例如,字符在另存为UTF-8的文件中保存为3个字节,但在另存为GBK的文件中保存为2个字节:

>>> u'严'.encode('utf8')
'\xe4\xb8\xa5'
>>> u'严'.encode('gbk')
'\xd1\xcf'

如果声明了错误的编码,则字节将被错误地解释,Python将显示错误的字符或引发异常。

按评论编辑

2(1)-这取决于系统,因为ANSI是系统区域设置的默认编码。在我的系统中,cp1252和记事本++不能显示中文字符。如果我将系统区域设置设置为Chinese(PRC),那么我将在控制台终端上获得您的结果。在这种情况下,它正常工作的原因是使用了字节字符串,并且字节只是发送到终端。由于文件是在Chinese(PRC)区域设置的ANSI中编码的,因此Chinese(PRC)区域设置终端会正确解释字节字符串包含的字节。

2(2)-文件用UTF-8编码,但编码声明为GBK。当Python读取编码时,它试图将文件解释为GBK,但失败了。您选择了UTF-8作为编码,在记事本++中,它还包括一个UTF-8编码字节顺序标记(BOM)作为文件中的第一个字符,而GBK编码解码器没有将其作为有效的GBK编码字符读取,因此在第1行失败。

2(3)-文件使用UTF-8编码(带物料清单),但缺少编码声明。Python识别UTF-8编码的BOM并使用UTF-8作为编码,但是文件是GBK格式的。因为使用了字节字符串,所以UTF-8编码的字节被发送到GBK终端,您将得到:

>>> u'严'.encode('utf8').decode(
'\xe4\xb8\xa5'
>>> '\xe4\xb8'.decode('gbk')
u'\u6d93'
>>> print '\xe4\xb8'.decode('gbk')
涓

在这种情况下,我很惊讶,因为Python忽略了字节\xa5,正如您在下面看到的,当我显式解码错误时,Python抛出一个异常:

>>> u'严'.encode('utf8').decode('gbk')
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa5 in position 2: incomplete multibyte sequence

2(4)-在这种情况下,编码是ANSI(GBK),但没有声明编码,而且在UTF-8中没有类似BOM的BOM来给Python一个提示,因此它采用ASCII,并且不能处理第3行的GBK编码字符。

相关问题 更多 >