Jun 15, 2011

python str, unicode, bytearray 以及处理 utf8, gbk

# -*- coding: gbk -*-
# -*- coding: gb2312 -*-
# -*- coding: utf_8 -*-       (utf8 utf_8 utf-8 都可以)
全部 coding 列表

参见
Unicode, UTF-16, UTF-8, GBK, 系统如何处理
python的str,unicode对象的encode和decode方法

很好的阅读资料
Dive into Python 3. Chapter 4. Strings.
Chapter 11. Files

Python 2.x 和 Python 3 在这方面的处理差异很大。

Python 2.x

python2.x 有 str 和 unicode。
str 是常用的类型,以字节为单位。unicode 以字符为单位。

区分编码(gbk, utf8)和容器(str, unicode)。str 有不同的编码,可以是gbk的,也可以是utf8的。unicode 也一样。

'我们'是 str
u'我们'是 unicode
如果二者编码相同,则字节流是一致的,只不过“看法”不同。str 是以字节为单位,unicode 以字符为单位。

一般来说,python 2.x 程序中 str 是大家都接受的,是很多程序接口的变量类型。但是有时需要不同的编码,譬如获得了一个gbk编码的str,但是后续处理需要 utf8 编码的str,所以需要转换。转换是通过 unicode 来做中间的桥梁,涉及两个函数:
s.decode(e) --> u      等价于 u = unicode(s,e)
u.encode(e) --> s
e 为编码
可以看出,encode和decode的说法围绕着 str,如果encode看作是一种加密的话,unicode是明文,str是密文。python2.x 的世界,大家比较钟情于用密文来交流。
注:也有 u.decode 和 s.encode,含义见这篇文章,一般不需要用

所以一个 gbk 的str s1 转化为 utf8的str s2 的做法:
s2 = s1.decode('gbk').encode(utf8)

而将一个 gbk 的unicode u1 转化为 utf8的unicode u2 的做法:
u2 = u1.encode('utf8').decode('utf8')

可见实现转码功能的是 u.encode 函数,decode 没有转码功能

.py 文件

采用在.py文件开头写入
# -*- coding: gbk -*-
规定 .py 文件的编码,如果没有规定,则默认为系统编码

但上面只是规定编码,并没有规定容器。所以
'我们' 是 str
u'我们' 是 unicode

目录名和文件名操作,读写文件

python 2.x 和系统打交道的命令,一般输入输出都是 str,编码为系统默认编码。

对于默认为gbk的中文系统,python2.x 操作目录名和文件名时,参数为 gbk编码的 str
譬如
open(filename)
os.listdir(dirname)
filename 和 dirname 都是gbk编码的str,os.listdir() 的返回结果也是 gbk编码的str(假设文件名包含中文)


读写文件
字符文件有编码,一般头几个字节代表编码,如果没有,视为系统默认编码,如 'gbk'。
ignore 的必要性


open() 命令,并没有 encoding 选项
读文件,就是读成字节 str,写文件也是写入 str

open().read() 读成字节str
file.write() 也需要字节 str

print str
对于中文系统,若输出到 console,也 str 必须为 gbk 编码,如是 utf 8 编码会出现乱码
print unicode
可以打印 utf8 编码的 unicode,不会出现乱码

其他

遍历字符
因为 gbk, utf-8 编码字符占长不一,所以要 decode,才能遍历

比较字符,比如验证 gbk  unicode 字符 c 是不是中文的“是”,可以用 unicode比较,也可以用 str比较
  1. c == u'是',前提是 .py 文件编码为 gbk,等同于 c == '是'.decode('gbk')
  2. c.encode('gbk') == '是'
另两个函数
  1. ord(字符) 字符的 unicode 代码,此处为统一的代码,可能是 utf8 的代码
  2. chr(Ascii编码), unichr(unicode 编码) 返回字符
ord(u'我') 和 orf(u'我'.encode('utf8').decode('utf8')) 返回是一样的
注意 ord('是') 是有错的,因为 '是' 是两个字节

例子,读取文件(内容为一个中文字符‘是’)
# -*- coding: gbk -*- 

f = open('c.txt','r')
content = f.read()
print f.encoding
f.close()

print len(content)
print content
print hex(ord(content[0]))
print hex(ord(content[1]))

content = content.decode('gbk')
print len(content)
print content
print hex(ord(content))

if content == '我'.decode('gbk'):
 print True

f = open('c2.txt','w')
content = content.encode('gbk')
f.write(content)
f.close()

python 3

python2.x中正室是str,小妾是 unicode,大家常和 str 打交道。python 3 把小妾 unicode 扶持成了正室,并改头换面命名为 str,而原来正室退化成 bytearray。
python 3 中

  1. str,相当于 2.x 的unicode
  2. bytearray,相当于 2.x 的 str
  3. 大家常用 str
所以 
'我们' 是 str,长度为 2
b = bytearray(s, encoding)
b = bytearray('我们', ‘utf8’)

python .py 文件

默认为 UTF-8 编码,所以用 ue 编写时,要保存为 utf8

如果不是,要加入说明,比如
# -*- coding: gbk -*-

而对于读写文件,默认编码为系统默认编码,如果不是,要在 open 时说明,如
a_file = open('examples/chinese.txt', encoding='utf-8')

0 comments: