python 字符编码

目录

1 python2.7 中的字符编码问题

1.1 ascii, unicode, utf8

1.2 encoding声明

1.3 python2.7中的str和unicode

1.4 python2.7中的encode和decode

1.5 修改系统默认编码

1.6 查看文件编码

1.7 文件读写

1.7.1 内置的默认open

1.7.2 模块codecs中的open

1.8 一般的处理要点

2 python3字符编码转换

2.1 源码文件默认编码

2.2 Pycharm 设置步骤

2.3 字符编码转换


1 python2.7 中的字符编码问题

1.1 ascii, unicode, utf8

  • ascii码:最早的编码,只有127个字符,包含英文字母,数字,标点符号和一些其它符号。一个字节表示一个字符
  • unicode(统一码):一个字节不够放,全世界有各种语言的字符需要编码,于是unicode给所有的字符都设定了唯一编码。通常都是用两个字节表示一个字符(有些生僻的字要用四个字节)。所以,要理解一点:下文中提到到的unicode编码是双字节编码(一个字符 两个字节)。
  • uft8:对于ascii编码的那些字符,只需要1个字节,unicode给这些字符也设定2个字节,如果一篇文章全是英文(ascii字符),就浪费了很多空间(本来1个字节可以存储的,用了2个字节),所以产生了utf8。utf8是一种变长的编码方式,根据不同的符号变化字节长度,把ascii编码成1个字节,汉字通常编码成3个字节,一些生僻的字符编码成4~6个字节。

        在计算机内存中,统一使用 Unicode编码 

        在python中,建议程序过程中统一使用unicode编码,保存文件和读取文件时使用utf-8(在读写磁盘文件时候用utf8进行相应的decodeencode,关于decodeencode见下文1.4 )。

        要记住:所有的编码方式都是向后兼容ASCII的。ASCII字符对应什么二进制,那么在其他编码方式中也对应同样的二进制。

注:

        能用到向后兼容向前兼容, 那么肯定是存在接口概念的。 也就是说存在一个系统提供接口供外部使用, 外部应用使用这个接口。 然后就存在系统迭代周期应用迭代周期不一致的问题,这时候就出现了向前兼容向后兼容的说法。

  • 向前兼容的英文为 Forwards Compatibility,Forward有“将来”的含义。因此向前兼容就是指:以前的版本支持现在版本生成的数据,现在的版本支持以后的版本数据。
(1)“Windows 3.1要能运行为Windows 10开发的程序” 
    (应用使用了一些新的接口,但是系统却是旧的系统)

(2)“Word 2003 能够打开用Word 2007创建的文件。”
    (Office是系统,文件就是使用接口的应用。Office 2007的文件就是使用了新的接口 , Office 2003 就是旧的系统)

(3)“USB3.0的U盘,插在USB1.0的接口上”

  • 向后兼容的英文为Backwards Compatibility,Backward有“回头”的意思。所以向后兼容就是指现在的版本可以支持以前的版本数据。
(1)“Windows 10要能运行为Windows 3.1开发的程序”  
    (在这里,Window是系统,开发的程序就是使用系统接口的应用。应用使用了旧的接口,系统是新的系统)

 (2)“RFC2018的设备能够兼容RFC793协议”
    (在这里,使用TCP协议的设备都是系统,但是存在一些设备使用了 RFC2018协议, 一些设备使用了 RFC93协议 。使用了RFC93协议的设备就是使用了旧的接口,使用了 RFC2018协议的设备就是新系统)

 (3)“2007 Microsoft Office 系统能够打开 Office 2000和Office 2003的文件”  
     (在这里,Office是系统,文件就是使用接口的应用。Office 2000和Office 2003的文件就是使用了旧的接口 , Office 2007 就是新的系统)

 (4)“CD盘可以放在CD光驱播放,也可以放在DVD光驱播放。”
     (光驱是系统,CD盘是使用接口的应用。CD是旧接口, DVD光驱是新系统)

兼容性是形容系统的。 

  • 系统向后兼容 , 也就是兼容以前的 , 就说明系统是相对新的。 
  • 系统向前兼容 , 也就是兼容未来的 , 就说明系统是相对旧的。

1.2 encoding声明

      python默认使用ascii编码去解释源文件。

import sys
sys.getdefaultencoding()
# 'ascii'

      如果源文件中出现了非ASCII码字符,不在开头声明encoding会报错。

      可以声明为utf8,告诉解释器用utf8去读取文件代码,这个时候源文件有中文也不会报错。

# encoding=utf8 如果不加这一行会报错
print '解释器用相应的encoding去解释python代码'

在文件开头加入# -*- coding: UTF-8 -*- 或者 # coding=utf-8 。

注意:# coding=utf-8 的 = 号两边不要空格。

1.3 python2.7中的str和unicode

        debugger的时候会发现,python2.7中的字符串一般有两种类型,unicode和str

        str为字节码,会根据某种编码把字符串转成一个个字节,这个时候字符字节没有所谓固定的一一对应的关系。

        unicode则是用unicode编码的字符串,这个时候一个字符对应两个字节的,一一对应。

        直接赋值字符串,类型为str,str为字节串,会按照开头的encoding(即 # encoding=utf8 等)来编码成一个个的字节。

        赋值的时候在字符串前面加个u,类型则为unicode,直接按照unicode来编码。

  默认使用ascii编码时:

s1 = '字节串'
print type(s1) #输出 <type 'str'>,按照开头的encoding来编码成相应的字节。
print len(s1) #输出9,因为按utf8编码,一个汉字占3个字节,3个字就占9个字节。

s2 = u'统一码'
print type(s2) #输出 <type 'unicode'>,用unicode编码,2个字节1个字符。
print len(s2) #输出3,unicode用字符个数来算长度,从这个角度上看,unicode才是真正意义上的字符串类型

  比如我们要从一个文件中找出中所有后两位是'学习'的词语,再进行判断的时候: 

s = '机器学习'
s[-2:] == '学习‘ 
# 返回false,平时写程序可能会以为相等。
# 这里的”学习是用开头的encoding声明解释的,我开头用的是utf8,汉字占3个字节,所以“学习”占了6个字节),而s[-2:]取的是最后两个”双字节“,所以不相同。

s = u'机器学习'
s[-2:] == u'学习’ 
# 返回true,这也是为什么说unicode是真正意义上的字符串类型。因为使用的是unicode,”学习“占的是两个”双字节“,一个"双字节“一个字。

        对于经常处理中文字符串的人,统一用unicode就可以避免这个坑了。

        虽然有些字符串处理函数用str也可以,应该是函数里面帮你处理了编码问题。

注:

       关于用re模块的正则查找方法search时总是找不出来(找错了或者出乱码)的问题。用utf8编码str类型的字符串在search方法中行不通,因为str字节串,和字符之间没有固定的一一对应的关系,正则没法用字节串来进行正确匹配。把正则式和目标字符串都使用unicode类型,unicode字符之间是两个字节对应一个字符的关系,正则可以根据这个来对字符进行匹配。

1.4 python2.7中的encode和decode

  • encode的正常使用:对unicode类型进行encode,得到字节串str类型。也即是unicode -> encode(根据指定编码) -> str
  • decode的正常使用:对str类型进行decode,得到unicode类型。也即是str -> decode(根据指定编码) -> unicode

 注意:encode和decode的时候都是需要指定编码的。

        因为在编码的时候要知道原来的编码是什么和按照什么新编码方式进行编码,要用到两种编码,这里默认有一个unicode,所以需要再指定一个编码方式。解码的时候也是一个道理。

这两个方法就是在unicode和str之间用指定编码进行转换。

s3 = u'统一码'.encode('utf8')
print type(s3) # 输出 <type 'str'>

s4 = '字节串'.decode('utf8')
print type(s4) #输出 <type 'unicode'>
  • encode的不正常使用:对str类型进行encode,因为encode需要的是unicode类型,这个时候python会用默认的系统编码decodeunicode类型,再用你给出编码进行encode。(注意这里的系统编码不是开头的encoding,具体例子见下文第5点)
  • decode的不正常使用:对unicode类型进行decodepython会用默认的系统编码encodestr类型,再用你给出的编码进行decode。

      所以改好对应的系统默认编码,就算不正常使用,也不会报错啦。不过多拐了一下路。

1.5 修改系统默认编码

      系统默认使用ascii编码,需要进行相应的修改。

      这个编码和开头的encoding不同之处在于,开头的encoding是对于文件内容的编码。

      这里的编码是一些python方法中默认使用的编码,比如对str进行encode的时候默认先decode的编码,比如文件写操作write的encode的编码(关于文件读写见下文第7点)

import sys
reload(sys)
sys.setdefaultencoding('utf8')

s = '字节串str'

s.encode('utf8')
#等价于
s.decode(系统编码).encode('utf8')

关于系统默认编码发挥作用的地方,来看看另一个例子。

import sys
print sys.getdefaultencoding()  # 输出ascii

s = 'u华南理工大学'
print s[-2:] == '大学'   # 返回False,并有warning提醒

reload(sys)
sys.setdefaultencoding('utf8')

print s[-2:] == '大学'  # 返回True 

        根据结果得知:python在用==比较时,如果第一个操作符是unicode而第二个不是的话,会自动用系统默认编码第二个操作符decode

        PS:为什么需要reload(sys)呢。首先,reload是用于重新加载之前import的模块。

        这里需要重新加载sys的原因是:python在加载模块时候删除了sys中的setdefaultencoding方法(可能是出于安全起见),所以需要reload这个sys模块。

       这里再举个简单例子,比如要修改keras的后端,从tensorflow改成theano,修改后需要重新加载keras的backend模块才能修改成功。

import keras.backend as K
k.backend()    # 一开始是u'tensorflow'
import os
os.environ['KERAS_BACKEND'] = 'theano'
K.backend() # 修改后还是u'tensorflow'
reload(K)
k.backend() # reload之后后端才变成u'theano'

1.6 查看文件编码

import chardet
with open(filename,'r') as f:
    data = f.read()
    return chardet.detect(data)

1.7 文件读写

       首先要记住,读出和写入,这两个文件的关口都是用str类型的,就是一个个字节

1.7.1 内置的默认open

       python中内置的默认open在读取文件的时候以字节串str的形式,读出一个个字节。读取后要用正确的编码才能decode成正确的unicode,所以要知道原来在文件中的编码。

       写文件的时候也是一个道理,用str类型,以字节的形式写入,这个str是以某种编码方式编码的,要注意用正确的编码方式编码,一般是按utf8编码后写文件。

       如果你用unicode类型写入,python会根据系统默认编码来把unicode编码成str再写入文件。因为写入文件需要的是str,是str就写,不是就把你转成str再写。

       简单原则,尽量用str写入,避免使用默认编码,这样也不用在开头修改默认编码。

1.7.2 模块codecs中的open

       python中模块codecs中的open方法可以指定一个编码。它保证了读入和写出的字节都是按照这个指定编码进行编码的。这样在读文件的时候:会把读出的str按照指定编码decode成unicode。

        写文件的时候:如果是unicode,会根据指定编码encodestr然后写入;如果是str,会根据系统默认编码把str进行decode得到unicode,再根据指定编码encodestr进行写入。

        简单原则,尽量用unicode写入,避免使用默认编码,这样也不用在开头修改默认编码。

PS:注意一下,对于其它方式读写文件,需要自行debugger看看编码的问题。比如在python中读取excel的时候读出来就直接是unicode而不是str

1.8 一般的处理要点

(1) 首先把源文件的默认encoding和系统默认编码改为utf8

(2) 程序执行过程统一使用unicode类型

(3) 对于读写文件(用python内置的默认open来说),得到的是str,str进行相应的encodedecode就可以了。

总结一下就是:

  • 设置相应的默认编码为utf8;
  • 读文件拿到str类型:str -> decode('utf8') -> unicode
  • 程序处理:用unicode
  • 写文件:unicode -> encode('utf8') -> str,用str类型写入文件
  • 当然前提是文件都是utf8格式的啦,包括源文件和读写的数据文件。

另外想说一下:

       对于写程序的过程中统一使用unicode类型这一点只是一个建议,因为统一unicode可以在处理字符串的时候减少麻烦。

      觉得全部弄成unicode麻烦的,可以考虑平时统一用utf8编码的str,有些问题需要用unicode的再转为unicode,遇到编码问题时可以思考是不是没有统一用unicode的问题(本文开头就给出了一个需要统一用unicode的情况)

2 python3字符编码转换

2.1 源码文件默认编码

Python3.X 源码文件默认使用utf-8编码,所以可以正常解析中文,无需指定 UTF-8 编码

注意:如果你使用编辑器,同时需要设置 py 文件存储的格式为 UTF-8,否则会出现类似以下错误信息:

SyntaxError: (unicode error) ‘utf-8’ codec can’t decode byte 0xc4 in position 0:
invalid continuation byte

2.2 Pycharm 设置步骤

  • 进入 file > Settings,在输入框搜索 encoding
  • 找到 Editor > File encodings,将 IDE Encoding 和 Project Encoding 设置为utf-8。

python 字符编码

2.3 字符编码转换

        PYTHON编码类型默认是UTF-8,然而在工作中经常会遇到不同编码的问题,这时就需要对不同的编码进行转换,在python中不同编码的转换都要经过Unicode,不论是UTF-8或者是GBK,整个过程都是先通过编码decode转换为Unicode告诉Unicode当前的编码格式是什么、然后再通过解码encode转换为自己想要实现的编码格式或类型。

Python 3.7.3:

import sys
sys.getdefaultencoding()
# 'utf-8'

python 字符编码

Unicode字符串可以用多种方式编码为普通字符串,假设unicodestring = u"Hello world",依照所选择的编码(encoding),如下:

1 #将Unicode转换成普通的Python字符串str:"编码(encode)"。

utf8string = unicodestring.encode("utf-8")
asciistring = unicodestring.encode("ascii")
isostring = unicodestring.encode("ISO-8859-1")
utf16string = unicodestring.encode("utf-16")

2 # 将普通Python字符串str转化为Unicode:"decode"

plainstring1 = unicode(utf8string, "utf-8")
plainstring2 = unicode(asciistring, "ascii")
plainstring3 = unicode(isostring, "ISO-8859-1")
plainstring4 = unicode(utf16string, "utf-16")

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

上一篇:模块(二)


下一篇:PHP FILTER_SANITIZE_STRING 过滤器