python unicode 和string那📺

开发过程中总是会碰到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, 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

常见问题#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其实搞清楚之后来来回回就是那些小问题, 希望对大家有帮助.

 

================================================================================================================================================

一、

在爬虫抓取网页信息时常需要将类似"\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8"转换为中文,实际上这是unicode的中文编码。可用以下方法转换:

1、
1 >>> s = u'\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8'

2 >>> print s

3 人生苦短,py是岸

2、1 >>> s = r'\u4eba\u751f\u82e6\u77ed\uff0cpy\u662f\u5cb8'

2 >>> s = s.decode('unicode_escape')

3 >>> print s

4 人生苦短,py是岸

二、

另外,在python2的字符编码问题时常会遇到“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)”的编码错误。

而用以下方法通常可以解决:

1 import sys
2 reload(sys)
3 sys.setdefaultencoding('utf-8')
  此方法是将Python2的默认编码ASCII改为 utf-8。但此方法不是一劳永逸的,可能会使一些代码的行为变得怪异。




我们只需要努力,然后剩下的交给时间。