python2与python3的编码问题

无论是python2还是python3都可以理解成是Unicode编码;

但是在电脑硬盘上存储是按照不同的映射关系的。

首先了解下:

python的encode和decode 首先明白一件事情,之前说过Unicode将所有的字符都对应上了相应的码点,而UTF-8或者ASCII码不过是对应从Unicode到字节的映射方式,既然有映射方式,那么就有映射方向。我们把从Unicode到字节码(byte string)称之为encode,把从字节码(byte string)到Unicode码称之为decode

 

一、python2注意事项

python2中,有两种不同的字符串数据类型,

一种是 “str”对象,字节存储,

另一种:如果在字符串前使用一个’u’的前缀,表示的是这个字符的Unicode码点:

 

1. Python2 处理英文字符

>>> str='hello'            #首先’hello'字符串的每个英文字符变换成unicode编码;在存储时python2对英文按照ascii射的方式编码存储5个字节编码
>>> str              
'hello'
>>> type(str)               #此时赋值给str,就是第一种: “str”对象,存储着字节
<type 'str'>

>>> len(str)                #len字符数,就是一字节一英文字符
5
>>> str.decode('ascii')     #'ascii'解码:按照ascii规则吧str英文字符由字节编码映射成字符编码,也就是把str对象类型变为unicode字符码
u'hello'
>>> str.decode('uTf-8')     #'utf-8'解码:按照utf-8规则吧str英文字符由字节编码映射成字符编码,也就是把str对象类型变为unicode字符码
u'hello'
                          #结果为什么结果一样,因为对待英文字符AscII和utf-8都是按照一个字节存储的, 没有什么差别
>>> print(u'hello')        #关于print 函数,无论字符unicode编码还是字节ascii编码都能正常输出
hello
>>> print(str)
hello
>>>

 

2. Python2处理中文字符

  • 直接输入

>>> str="中国"          #中文在python2中是按照gbk编码的; 
>>> type(str)
<type 'str'>
>>> str                 #str仍然是一个str对象(字节存储)
'\xd6\xd0\xb9\xfa'
>>> len(str)             #len函数处理str对象,是统计str字节的个数;因为str是字节编码的
4


'''以下是对str对象的解码操作;不难发现中文在python2中是按照gbk编码的'''


>>> str.decode('ascii')    #'ascii'解码报错

Traceback (most recent call last):
 File "<pyshell#16>", line 1, in <module>
   str.decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd6 in position 0: ordinal not in range(128)
>>> str.decode('utf-8')   #'utf-8'解码报错

Traceback (most recent call last):
 File "<pyshell#17>", line 1, in <module>
   str.decode('utf-8')
 File "C:\Python27\lib\encodings\utf_8.py", line 16, in decode
   return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0xd6 in position 0: invalid continuation byte
>>> str.decode('gbk')       #'gbk'解码成功
u'\u4e2d\u56fd'


>>> str1= str.decode('gbk')    #str(gbk)字节编码 解码为 unicode字符编码 存到str1
>>> str1.encode('ascii')       #str1 (unicode字符编码) 按照'ascii'编码   出错
#显然:ascii编码并不能处理unicode编码的中文,所以出错

Traceback (most recent call last):
 File "<pyshell#25>", line 1, in <module>
   str1.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
   
'''对于unicode编码的中文在python2中是按可以用'gbk','utf-8'编码的,但是不能被'ascii'编码 '''


>>> str1.encode('gbk')  #'gbk'编码
'\xd6\xd0\xb9\xfa'
>>> str1.encode('utf-8')   #'utf-8'编码
'\xe4\xb8\xad\xe5\x9b\xbd'


'''print输出语句:再次证明:无论是unicode还是utf-8,gbk的字节编码都可以很好的输出'''

>>> print(u'\u4e2d\u56fd')
中国
>>> print('\xd6\xd0\xb9\xfa')
中国
>>> print('\xe4\xb8\xad\xe5\x9b\xbd')
中国
>>> str3=str1.encode('utf-8')   #又一次证明:len函数对于字节编码,按照字节算个数
>>> len(str3)
6
  • u'字符串输入'

>>> str=u'中国' 
>>> str
u'\u4e2d\u56fd'
>>> type(str)
<type 'unicode'>
>>> len(str)         #又一次证明:len函数对于unicode编码,按照字符个数算
2

字符串前加个u,变量按照unicode存储的。两个字节一个中文/英文字符。

所以,对于python2,强烈建议在输入字符串加个u,按照unicode编码存储。

 

 

3. Python2程序开头写#coding=utf-8的作用

Python文件编译最终还是要转换成字节码,Python2程序开头写#coding=utf-8的作用其实就是把这个Python程序文件按照utf-8编码的方式映射到字节码,如果不加这个开头,程序里面的中文会按照Python默认的ascii码方式encode,这个肯定是要报错的,大家都知道,如果程序里写了中文但是没有加这个开头,那么pycharm会在一开始就报错,也是这个道理。加了这个开头之后,程序里面的字符都将会使用utf-8编码的方式被映射到字节码,也就是上一个大节里面的byte string,值得注意的是,程序中的汉字将会以utf-8的形式编码成为字节码,因此如果需要将其decode到Unicode字符,也是需要使用utf-8方式decode。

 

4. python2因为ascii对中文(unicode)编码解码错误

  1. 错误写法:

    原因:开头并没有改变Python的系统编码方式:Python2默认的编码解码方式是ascii码

#coding=utf-8         #中文赋值按照utf-8码方式encode

s='中国'
s_decode=s.decode('utf-8')
ps=s_decode+s      #字节编码与字符编码拼接最后是 字符编码;所以s字节编码需要解码,这时按照ascii码
print ps                      #   中文不能被'ascii'解码编码 ,出错                                                        

Traceback (most recent call last):
   ps=s_decode+s
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

s_decode是Unicode字符,ss是字节字符(byte string),两者相加最后的结果仍然是Unicode字符

  1. 解决办法:正确写法
>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>> sys.setdefaultencoding('utf-8')
>>> s_decode+s
u'\u4e2d\u56fd\u4e2d\u56fd'

这个代码将Python的编码默认编码方式由ascii码换成了utf-8编码,因此s_decode+s这个就不像上面一样报错了。

或则显示指明:

#coding=utf-8
s='中国'
s_decode=s.decode('utf-8')
ps=s_decode+s.decode('utf-8')
print ps              

python test.py
中国中国

 

3. 补充

>>> f=open('test.txt','rb')
>>> s=f.read()
>>> s
'\xe6\x88\x91\xe7\x88\xb1\xe4\xb8\xad\xe5\x9b\xbd\n'
>>> s.decode('utf-8')
u'\u6211\u7231\u4e2d\u56fd\n'
>>> print s.decode('utf-8')
我爱中国
>>> print s
我爱中国

显然f.read()读出来的是字节码

 

二、python3注意事项

在python3 中 Python 3 也有两种类型,一个是 str(unicode), 一个是 byte 码。但是它们有不同的命名。 type(变量)

Python 3 中对 Unicode 支持的最大变化就是没有对 byte 字符串的自动解码

如果你想要用一个 byte 字符串和一个 unicode 相连接的话,你会得到一个错误,不管包含的内容是什么。 可以简单理解为: python2 中的unicode -> python3 的str python2 中的str-> python3 的byte

1. python3处理中文

>>> s="中"
>>> type(s)
<class 'str'>
>>> s            
'中'
>>> len(s)   #s是unicode编码 字符编码
1
>>> s.encode('utf-8')
b'\xe4\xb8\xad'

'''任何中文的unicod编码,都不能用ascii编码/解码'''


>>> s.encode('ascii')
Traceback (most recent call last):
 File "<pyshell#5>", line 1, in <module>
   s.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u4e2d' in position 0: ordinal not in range(128)
   
   
>>> s1=s.encode('utf-8')
>>> s1
b'\xe4\xb8\xad'
>>> type(s1)
<class 'bytes'>
>>> s1.decode('utf-8')   #只要是unicode(str对象)就可以直接输出
'中'


'''对于byte.decode()解码:python3不支持二进制形式解码,python2支持'''

#python3
>>>'\xe4\xb8\xad'.decode('utf-8')
Traceback (most recent call last):
 File "<pyshell#11>", line 1, in <module>
   '\xe4\xb8\xad'.decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
   
#python2  
>>> '\xe4\xb8\xad'.decode('utf-8')
u'\u4e2d'

#就是那么神奇!!!


'''===============print()输出函数================       '''
'''
对于byte字节
二进制形式输出:不加b:乱码;   加b 当字符串输出
  用变量存储输出: 二进制输出
对于unicode(str):
直接输出汉字/英文
'''
>>> print('\xe4\xb8\xad')   #utf-8
ä¸
>>> print(b'\xe4\xb8\xad')
b'\xe4\xb8\xad'

>>> print (s)         # s: unicode  


>>> print(s1)           # s1变量   utf-8存储
b'\xe4\xb8\xad'

>>> s2=s.encode('gbk')
>>> s2
b'\xd6\xd0'

>>> print('\xd6\xd0')
ÖÐ
>>> print(b'\xd6\xd0')
b'\xd6\xd0'

文件前面 加 #coding : utf-8 无用

2. python3处理英文字符

>>> s='dv'
>>> s
'dv'
>>> len(s)
2
>>> s.encode('utf-8')
b'dv'
>>> s1= s.encode('utf-8')
>>> s2=s+s1
Traceback (most recent call last):
 File "<pyshell#5>", line 1, in <module>
   s2=s+s1
TypeError: can only concatenate str (not "bytes") to str
>>> s3=s+s1.decode('utf-8')
>>> s3
'dvdv'

3. 输出print()函数

print (s1+s2)

s1与s2必须是str(unicode)型。不然连接报错,如上;

三、编码原由

ASCII编码 最早出现的是ASCII码,使用8位二进制数组合表示128种字符。因为ASCII编码是美国人发明的,当初没考虑给别的国家用,所以,它仅仅表示了所有美式英语的语言字符。但是没有使用完。

ISO 8859-1/windows-1252 128位字符满足了美国人的需求,但是随之欧洲人加入互联网,为了满足欧洲人的需求,8位二进制后面还有128位。这一段编码我们称之扩展字符集,即ISO 8859-1编码标准,后来欧洲的需求变更,即规定了windows-1252代替了ISO 8859-1

GB2312 然后当我国加入后,8位二进制(即一个字节)用完了,于是我们保留ASCII编码即前128位,后面的全部删除。因为我国得语言博大精深,所以需要2个字节,即16位才能满足我们得需求,所以当计算机遇到大于127的字节时,就一次性读取两个字节,将他解码成汉字。即GB2312编码

GBK 相当于GB2312的改进版,增添了中文字符。但还是2个字节表示汉字

GB18030 为了满足日韩和我国的少数民族的需求,对GBK的改进,使用变长编码,要么使用两个字节,要么使用四个字节。

Unicode 虽然每种编码都兼容ASCII编码,但是各个国家是不兼容的。于是出现了Unicode,它将所有的编码进行了统一。它不能算是一种具体的编码标准,只是将全世界的字符进行了编号,并没有指定他们具体在计算机种以什么样的形式存储。 它的具体实现有UTF-8,UTF-16,UTF-32等。

 

posted on 2020-07-13 20:59  郑爱澳  阅读(449)  评论(1编辑  收藏  举报