目录
Python 中文编码 Python 中文编码是新人常见的一个问题,其实这是一个涉及到 Python 字符串、编码、IO 读写等方面的问题。
字符串和编码 计算机只能处理数字,文本需要转为数字。最早的计算机采用 8 个 bit(比特,计算机最小数据单位) 作为一个 byte(字节,存储和传输容量的一种基本计量单位)。所以一个字节表示的最大整数是 255(二进制:11111111 = 十进制 255:2^7 + 2^6 +…+ 2^1 +2^0),更大整数需要更多字节。
最早只有 127 个字母(英文的大小写字母和数字,一些符号等)被编码到计算机,这个编码表就是 ASCII
,没有中文。 中文至少需要两个字节,并且不能和已有的 ASCII
冲突,于是有了中国制定的 GB2312
编码,用来把中文编进去。
全世界很多语言,就会有很多编码,因此 Unicode
就出现了,从字面意思也可以看出这是一套“整合”的编码。不过由于中文等语言编码长度很长,就会导致只使用英文的文本多占用很多存储空间,在存储和传输上十分不方便。于是,出现了把 Unicode 转化为“可变长编码”的 UTF-8
编码。
UTF-8
编码把一个 Unicode 字符根据不同的数字大小编码成 1-6 个字节:
英文字母 1 个字节
汉字通常 3 个字节 这样,无论传输什么文本,UTF-8
都能节省空间。总结下就是,ASCII
实际可以看成是 UTF-8
的一部分,所以之前的软件现在也能使用,这个也可以看作是一种向下兼容。
存储和执行时的状态 在计算机内存中,统一使用 Unicode 编码;当需要保存到硬盘,或者在网络上传输时,就转为 UTF-8 编码
记事本编辑时,从文件读取的 UTF-8 字符被转为 Unicode 字符到内存里;编辑完成保存时,把 Unicode 转换为 UTF-8 保存
浏览网页时,服务器会把生成的 Unicode 转换为 UTF-8 再传输导浏览器,所以很多网页是用 UTF-8 编码的
Python 字符串 Python 诞生早于 Unicode 标准发布,所以最早只支持 ASCII 编码(不支持中文),后来增加了对 Unicode 的编码,就是看到的 u'...'
,就表示是用 Unicode 编码的
中文
u'\u4e2d'
u'A'
u'A'
u'中'
和 u'\u4e2d'
一致,\u
后面是十六进制的 Unicode 码。
对英文来说,字符串 'xxx'
可以看作是 ASCII 编码,也可以看作是 UTF-8 编码,但是 u'xxx'
一定是 Unicode 编码
Unicode 转换为 UTF-8 用 encode,相反则用 decode:
encode
'ABC'
'\xe4\xb8\xad\xe6\x96\x87'
(3, 3)
1 len(u'中文' ), len(u'中文' .encode('utf-8' ))
(2, 6)
decode
decode
可以把 UTF-8 表示的字符串转换为 Unicode 字符串
u'abc'
1 '\xe4\xb8\xad\xe6\x96\x87' .decode('utf-8' )
u'\u4e2d\u6587'
1 print '\xe4\xb8\xad\xe6\x96\x87' .decode('utf-8' )
中文
str
用 str 也可以把 unicode 重新编码 utf-8
'\xe4\xb8\xad\xe6\x96\x87'
1 str('\xe4\xb8\xad\xe6\x96\x87' )
'\xe4\xb8\xad\xe6\x96\x87'
type
unicode
str
'\xe4\xb8\xad\xe6\x96\x87'
1 type('\xe4\xb8\xad\xe6\x96\x87' .decode('utf-8' ))
unicode
实际程序中的编码 Python 源代码中的说明 在保存源代码时要声明程序的编码类型为 utf-8,这样当 Python 解释器去读取源代码时就会明白:
!/usr/bin/env python2.7
-*- coding: utf-8 -*-
第一行告诉 Linux/OS X 这是一个 Python 可执行程序 第二行告诉 Python 解释器,按照 UTF-8 读取源代码,否则源代码中的中文可能会乱码
要循环处理字符串的时候需要解码 1 2 3 4 5 6 token = ",.!??::" for t in token: print t
�
�
�
.
!
?
�
�
�
�
�
�
:
1 2 3 for t in token.decode('utf-8' ): print t
,
.
!
?
?
:
:
把字符串 decode 为 utf-8 后,就可以被遍历循环 了。
字符匹配的时候需要解码 1 2 3 4 5 6 7 8 9 10 11 token = ",。!?:;" .decode('utf8' ) string_list = ['我' , '是' , '中国' , '人' ,',' , '我' , '爱' , '她' , '。' ] for t in token: for s1 in string_list: if t == s1: print t
/home/evil_rabbit/anaconda2/lib/python2.7/site-packages/ipykernel/__main__.py:10: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
1 2 3 4 5 6 for t in token: for s1 in string_list: if t == s1.decode('utf-8' ): print t
,
。
'\xe6\x88\x91'
1 2 3 4 5 6 for t in token: for s1 in string_list: if t.encode('utf-8' ) == s1: print t
,
。
结巴分词 jieba 分词后词语变为 unicode 编码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import jiebastring = "我是中国人,我喜欢编程、音乐、运动、看书。" string_list = [] seg = jieba.cut(string) for word in seg: string_list.append(word) print string_list
[u'\u6211', u'\u662f', u'\u4e2d\u56fd', u'\u4eba', u'\uff0c', u'\u6211', u'\u559c\u6b22', u'\u7f16\u7a0b', u'\u3001', u'\u97f3\u4e50', u'\u3001', u'\u8fd0\u52a8', u'\u3001', u'\u770b\u4e66', u'\u3002']
去掉标点符号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import jiebastring = "我是中国人,我喜欢编程、音乐、运动、看书。" string_list = [] seg = jieba.cut(string) for i in seg: string_list.append(i) token = ",。!?:;、" .decode('utf-8' ) filter_seg = [fil for fil in string_list if fil not in token] for word in filter_seg: print word
我
是
中国
人
我
喜欢
编程
音乐
运动
看书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import jiebastring = "我是中国人,我喜欢编程、音乐、运动、看书。" string_list = [] seg = jieba.cut(string) for i in seg: string_list.append(i) token = ",。!?:;、" .decode('utf-8' ) for word in string_list: if word not in token: print word
我
是
中国
人
我
喜欢
编程
音乐
运动
看书
读存文档时的编码 读取文档注意事项 为了避免每次都要 close 文件,建议使用 with
1 2 with open('/./xx.txt' , 'r' ) as f: f.read()
read()
会一次性读取全部内容,如果文件太大,可以用 read(size)
读取 size 个字节的内容。 或者调用 readline()
按行读取;调用 readlines()
一次读取所有内容并按行返回 list
1 2 for line in f.readlines(): line.strip()
txt 文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import jiebastring = "我是中国人,我喜欢编程、音乐、运动、看书。" string_list = [] seg = jieba.cut(string) for i in seg: string_list.append(i) token = ",。!?:;、" .decode('utf-8' ) filter_seg = [fil for fil in string_list if fil not in token] f = open('output.txt' , 'w' ) for word in filter_seg: f.write(word.encode('utf-8' ) + ' ' ) f.close()
如果读取非 ASCII 编码的文本文件,可以用二进制模式打开,再解码;或者给 open
函数传入 encoing
参数
1 2 3 f = open('./xx.txt' , 'rb' ) u = f.read().decode('gbk' )
1 2 f = open('/./xx.txt' , 'r' , encoding='gbk' ) f.read()
遇到编码不规范的文件,可以给 open
函数一个 errors
的参数
1 f = open('/./xx.txt' , 'r' , encoding='gbk' , errors='ignore' )
codecs
python 的 codecs
模块可以帮助我们在读取文件时自动转换编码,直接读出 Unicode
1 2 3 4 import codecswith codecs.open('/./xx.txt' , 'r' , 'gbk' ) as f: f.read()
JSON
JSON 是不同语言的标准格式,速度快,而且可以直接在 Web 页面读取,非常方便。 因此,我们可以将一些需要调取的 dict 存储为 JSON。 另外一个原因,JSON 标准规定编码是 UTF-8,所以 Python 内置的 json
模块让我们很方便地在 Python 的 str
与 JSON 字符串之间转换。
1 2 3 import jsond = dict(name='Bob' , age=20 , score=88 ) json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
1 2 json_str = '{"age": 20, "score": 88, "name": "Bob"}' json.loads(json_str)
{u'age': 20, u'name': u'Bob', u'score': 88}
实际操作中,可以直接将 json.dumps(d)
写入 txt 文档,读取时 json.loads(f.read())
即可。
直接存储 Unicode 不将 Unicode 转为实际的文本存储字符集,而是将 Unicode 的内存编码值直接存储,读取文件时再反向转换回来
1 2 3 4 u'中文' .encode('unicode-escape' )
'\\u4e2d\\u6587'
1 2 3 4 '\\u4e2d\\u6587' .decode('unicode-escape' )
u'\u4e2d\u6587'
中文
直接存储 String 对于 UTF-8 编码的字符串,在存储时也可以直接存储编码值
1 2 3 4 '中文' .encode('string-escape' )
'\\xe4\\xb8\\xad\\xe6\\x96\\x87'
1 2 3 4 '\\xe4\\xb8\\xad\\xe6\\x96\\x87' .decode('string-escape' )
'\xe4\xb8\xad\xe6\x96\x87'
参考资料