每个用Python的人都肯定为编码头疼过,现在python2和3的str类型又有不同。所以专门写篇博客来记录python里关于Unicode的知识点和处理方法。
基础知识点
关于Unicode
- 我们通常意义上说的“字符”,是Unicode字符。
- 但计算机存储器里存的是字节序列,中间需要经过一层编码,并不是Unicode字符码位的二进制数据。
- Unicode字符包括一个前缀’U+’及字符码位,该码位与操作系统、编码方式无关,是Unicode可以跨平台的原因所在。
- Unicode码位是4-6位的16进制数字,但只有10%的码位会有对应字符。
- 把码位转换成字节序列的过程是编码(encode),把字节序列转换成码位的过程是解码(decode)。
- 我们通常说的编码是Unicode到字节码的过程,而不是从自然语言到Unicode的过程,某种意义上,我们可以认为Unicode字符是人类可读的。
- 字符的存储空间占用与Unicode无关,只和编码方式有关,但是在有的编码方式里,不同的码位字符占用空间可能不同(比如utf-8)。
- 原则上,我们无法在不知道编码方式的情况下,将字节码还原成字符。实际上我们可以根据一些特征猜测。
Python中的字符与Unicode
- Python出现的比Unicode标准还早几年,所以你得理解早期别扭的设计。
- Python2的str对象是字节序列。
- Python3的str对象是Unicode,约等于Python2里面的unicode类型。
- Python3里的字节序列类型是bytes,形如b’ab\xc4’,里面看似有ASCII字符,但实际存储的都是0-255的整数,只是能显示ASCII字符会显示出来。
- Python2默认的编码方式是ASCII,Python3是utf-8。
关于常见编码方式
- 最常见的编码方式有这几种:utf-8,utf-16,latin1,除此外还有针对中文的gb2312,微软的cp1252,IBM的cp437,现在几乎没用了,知道一下就行。
- 绝地大多数情况下,我们使用的都是utf-8。
- utf-16在开头会有形如b’\xff\xfeE\x00l\x00’的字节序列,是BOM,byte-order mark,字节序标记。大小字节序和CPU有关。使用确定字节序的编码方式UTF-16LE(小字节序),UTF-16BE(大字节序)可以避免出现BOM。
- utf-8的优势在于在不同平台的字节序都一致,根本不需要声明字节序,但Windows notepad还是会在utf-8的文件里增加BOM头b’\xef\xbb\xbf’,这很傻逼的处理方式会导致一些问题。
- gb2312是过时的中文编码方式,但很多文件(尤其Windows平台的)还会使用,如果中文用utf-8解析不出来,十有八九就是用的gb2312。
一些处理办法
处理时机
- 尽量保证字节码只出现在输入和最终输出的地方,业务逻辑里保证处理的都是unicode字符,可以极大的减少混乱度。
可以在入口处使用setdefaultencoding来直接改变默认编码方式,如下
1
2reload(sys)
sys.setdefaultencoding('utf-8')读取文件后尽早处理为unicode,或者直接开unicode通道。可以使用codecs库来直接开unicode通道,如下
file = codecs.open("test", "r", "utf-8")
特殊情况
其实编码导致的特殊情况有很多但我多数都忘了,记起来一个来补充一个
s = '\u5f20\u4e09'
形如这种情况,看似是个unicode,实际上s的类型是str,注意前面没有u,所以你继续用常用编码方式encode不会有任何改变,decode的话只会将\
转为\\
,结果还是无效,这时候需要的解码方法是s.decode('unicode_escape')
,可以将unicode码位的字符串转成真正的unicode对象
unicodedata库
如果你经常要处理非英文的拉丁语系语言,或者做搜索相关项目,这真是个好用到哭的python内置库。我深有感触。
- 形如
café
这种带着重音符号的拉丁文词组,想要当成英文处理,可以用unicodedata库的unicodedata.normalize('NFKD', 'café')
,将重音符号挑出来,然后再去掉特殊符号,从而变成ASCII范围内的英文。 unicodedata.normalize
第一个参数有4个可选项NFC/NFD/NFKC/NFKD
,NFC会把重音符合合并到字母上,NFD相反,会拆分出来,NFKC和NFKD同上,但是会做兼容处理。比如符号’μ’会转成希腊字母’μ’,还有二分之一那个符号(\u00bd),会被转成1/2。这对于提升搜索体验很有意义。unicodedata.name(uchar)
可以显示特殊字符的名称,也可以用unicodedata.lookup(name)
根据名称找到对应的字符。比如1
2>>> name(u'\u00bd')
'VULGAR FRACTION ONE HALF'unicodedata.numeric(uchar)
会将内容直接转成对应的数字,比如,这个函数也能将罗马数字、中文数字、中文繁体数字、特殊符号数字都转成标准float类型,如下:1
2
3
4
5
6
7
8>>> numeric(u'\u00bd')
0.5
>>> numeric(u'①')
1.0
>>> numeric(u'貮')
2.0
>>> numeric(u'三')
3.0unicodedata.category(uchar)
可以区分字符的类型,有以下几种:1
2
3
4
5
6Lu(letter,uppercase):大写字母。
Ll(letter,lowercase):小写字母。
Lt(letter,titlecase):词首字母大写的字母。
Lm(letter,modifier):修饰符字母字符,它是独立式的间距字符,指示前面字母的修改。
Lo(letter,other):不属于大写字母、小写字母、词首字母大写或修饰符字母的字母。
Nd(number,decimal digit):十进制数字字符,即范围 0 到 9 内的字符。
暂时就这些了,大家有想到的,或者发现的误区欢迎在评论区提出来。