开发过程中总是会碰到 string, unicode, ASCII, 中文字符等编码的问题, 每次碰到都要现搜, 很是浪费时间, 于是这次狠下心, 一定要搞清楚 python 的 string 和 Unicode 到底怎么回事.
基础知识
我们都知道计算机只认 0 和 1, 要想在计算机显示 26 个字母, 就要给他们一套映射规则: 计算机能认得的符号 --> 人类可读的符号. 这转换的过程就是一套编码规则.
字符集: 就是一套字符的集合(比如中文 4000 个汉字集合)
字符编码: 一套法则, 能够将 0/1 和人类的语言之间进行转换的法则
最初字符集比较少, ASCII 码就够用了(一些控制符和 26 个字母), 随着计算机的发展, 各国语言都有自己独特的编码, 汉字的编码也不断地扩展, 从 GBK 到 GB18030/DBCS. 这个时候 Unicode 应运而生.
Unicode 就是为了统一各国各地区的编码规则, 重新搞了一套包罗地球上所有文化, 符号的字符集! Unicode 没有编码规则, 只是一套包含全世界符号的字符集. Unicode 也不完美, 于是后续有了众多 UTF 编码(UTF-8, UTF-16).
总之搞清楚一件事情, 一个字符用了 UTF-8 编码的, 就要用 UTF-8 去解码, 不然就会出现乱码.
文本处理
在 python-2.x, 处理文本时, 有 string 和 unicode 两种类型
str 类型就是一串 bytes, 这种类型跟 C 语言中处理 string 是非常相似的
unicode 就是一串 unicode 的数字映射(code point), 用于映射某个字符与一个 unicode 的对应关系.
看看代码出来是如何的:
- >>> a = "简书"
- >>> type(a)
- <type 'str'>
- >>> a
- '\xe7\xae\x80\xe4\xb9\xa6'
- >>> print a
简书
- >>> u = u"简书"
- >>> type(u)
- <type 'unicode'>
- >>> u
- u'\u7b80\u4e66'
- >>> print u
简书
从上面的代码可以看到, a = "简书" 是 string 类型, 可以看到 a 是一串
'\xe7\xae\x80\xe4\xb9\xa6'
byte 字符, 而 u = u"简书" 是一串 \ uxxxx 的 unicode 数字, 通过 print a 和 print u 可以显示出中文字符.
常见问题 #1
大家经常犯的一个错误就是混淆了 unicode 以及通过 unicode 编码存储在 string 里面的类型.
比如上面的例子中 u'\u7b80' 是 unicode, '\xe7\xae\x80'是 byte string, byte 和 unicode 之间一一对应, 可以相互转换, 转换规则如下:
- >>> '\xe7\xae\x80'.decode('utf-8')
- u'\u7b80'
- >>> print '\xe7\xae\x80'.decode('utf-8')
简
- >>> u'\u7b80'.encode('utf-8')
- '\xe7\xae\x80'
- >>> print u'\u7b80'.encode('utf-8')
简
总结一下, 上面例子中
unicode 和 byte 都指简
byte string 里面存储的是 unicode 通过 utf-8 编码后得到的 bytes
所以 byte string 解码 (decode) 后即可得到 unicode
unicode 是 byte string 通过 utf-8 解码后得到的
unicode 用 utf-8 编码 (encode) 可以得到对应的 bytes
Note:
总而言之 Unicode ------ 编码 ------> byte string
Unicode <----- 解码 ------- byte string
Unicode 就像是加密传输中的明文, 可以用 UTF-8, UTF-16, UTF-7, UTF-32 等对 unicode 进行加密, 最后解密还是要用回原本的加密方式来解密, 不然就解出乱码啦.
常见问题 #2
对 unicode 或者 byte string 编码解码方向搞错
- >>> u '\u7b80'.decode('utf-8') Traceback(most recent call last) : File "<stdin>",
- line 1,
- in<module > File "/Users/serena/Documents/data-pipeline/data-ci-sqlbuffet-env/lib/python2.7/encodings/utf_8.py",
- line 16,
- indecode
- return codecs.utf_8_decode(input, errors, True) UnicodeEncodeError: 'ascii'codec can 't encode character u'\u7b80 ' in position 0: ordinal not in range(128)
- >>> '\xe7\xae\x80 '.encode('utf - 8 ')
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- UnicodeDecodeError: 'ascii ' codec can't decode byte 0xe7 in position 0 : ordinal not in range(128)
unicode 应该是进行编码的, 如果进行 decode, 是会出现 UnicodeEncodeError 异常的. bytes string 同理, 应该进行解码, 如果硬要进行编码的话, 则抛出 UnicodeDecodeError
常见问题 #3
API 调用不一致的问题. 在调用别人的 API 的时候, 需要看清楚是传 unicode 还是 byte string 作为参数. 因为第三方的 API 有的是支持 unicode, 有的是 byte string, 甚至有的两种类型都支持. 这个时候要清楚自己传进去的参数是什么, 比如一些变量值是从 http requests 里面拉过来的, 这个时候你获得的变量值很有可能是 unicode 类型(python requests get/post 把返回值都转成了 unicode), 而如果第三方的 API 需要 byte string, name 就需要自己判断一下并进行转换. 否则就会出现各种奇怪的 UnicodeError
虽然 python 社区规定了在所有的 API 中使用 unicode, 但是少数一部分的 API 处于安全考虑还是要求使用 byte string. 需要注意一下.
常见问题 #4
输出类型不一致.
既然 python 社区推动到处使用 unicode, 那么我们只要在开发过程中全部都转成 unicode 是不是就万事大吉了? 并不是, 当你要输出文本到 terminal 或者到文件, 这个文本必须是 byte string 类型的.
如果不是的话, python 会隐式地帮你将 unicode 转成 string, python 默认采用 ascii 编码, 而中文编码不在 ascii 编码能够表示的范围之内, 所以 string 无法将你好作为 ascii 编码保存为 str 类型
- >>> string = unicode('你好', 'utf8')
- >>> print string
你好
- >>> log = open('/var/tmp/debug.log', 'w')
- >>> log.write(string)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
所以当你需要输出的时候, 需要将你的 unicode 转换成 byte string 再写文件, 如果有中文的话, 要用'utf-8'或'GBK'等支持中文的编码.
>>> string.encode('utf-8')
python 2.x 的 unicode & str 其实搞清楚之后来来回回就是那些小问题, 希望对大家有帮助.
- Reference http: //blog.csdn.net/trochiluses/article/details/16825269
- http: //blog.csdn.net/trochiluses/article/details/8782019
- http: //www.cnblogs.com/skynet/archive/2011/05/03/2035105.html
- https: //pythonhosted.org/kitchen/unicode-frustrations.html
来源: http://www.jianshu.com/p/4ced2bbc9334