doubinning 2019-12-29
开发过程中总是会碰到string, unicode, ASCII, 中文字符等编码的问题, 每次碰到都要现搜, 很是浪费时间, 于是这次狠下心, 一定要搞清楚python 的string和Unicode到底怎么回事.
我们都知道计算机只认0和1, 要想在计算机显示26个字母, 就要给他们一套映射规则: 计算机能认得的符号 --> 人类可读的符号. 这转换的过程就是一套编码规则.
最初字符集比较少, ASCII 码就够用了(一些控制符和26个字母), 随着计算机的发展, 各国语言都有自己独特的编码, 汉字的编码也不断地扩展, 从GBK到 GB18030/DBCS. 这个时候Unicode应运而生.
Unicode就是为了统一各国各地区的编码规则, 重新搞了一套包罗地球上所有文化, 符号的字符集! Unicode没有编码规则, 只是一套包含全世界符号的字符集. Unicode也不完美, 于是后续有了众多UTF编码(UTF-8, UTF-16).
总之搞清楚一件事情, 一个字符用了UTF-8编码的, 就要用UTF-8去解码, 不然就会出现乱码.
在python-2.x, 处理文本时, 有string和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
可以显示出中文字符.
大家经常犯的一个错误就是混淆了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‘) 简
总结一下, 上面例子中
简
Note: 总而言之 Unicode ------编码------> byte string Unicode <-----解码------- byte string
Unicode就像是加密传输中的明文, 可以用UTF-8, UTF-16, UTF-7, UTF-32等对unicode进行加密, 最后解密还是要用回原本的加密方式来解密, 不然就解出乱码啦.
对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, in decode 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
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. 需要注意一下.
输出类型不一致.
既然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其实搞清楚之后来来回回就是那些小问题, 希望对大家有帮助.