我确定有很多关于Unicode和Python的说明,但为了方便自己的理解使用,我还是打算再写一些关于它们的东西。

字节流 vs Unicode对象

我们先来用Python定义一个字符串。当你使用string类型时,实际上会储存一个字节串。

[ a ][ b ][ c ] = 'abc'[ 97 ][ 98 ][ 99 ] = 'abc'

在这个例子里,abc这个字符串是一个字节串。97.,98,,99是ASCII码。Python 2.x版本的一个不足之处就是默认将所有的字符串当做ASCII来对待。不幸的是,ASCII在拉丁式字符集里是最不常见的标准。

ASCII是用前127个数字来做字符映射。像windows-1252和UTF-8这样的字符映射有相同的前127个字符。在你的字符串里每个字节的值低于127的时候是安全的混合字符串编码。然而作这个假设是件很危险的事情,下面还将会提到。

当你的字符串里有字节的值大于126的时候就会出现问题了。我们来看一个用windows-1252编码的字符串。Windows-1252里的字符映射是8位的字符映射,那么总共就会有256个字符。前127个跟ASCII是一样的,接下来的127个是由windows-1252定义的其他字符。

A windows-1252 encoded string looks like this:[ 97 ] [ 98 ] [ 99 ] [ 150 ] = 'abc–'

Windows-1252仍然是一个字节串,但你有没有看到最后一个字节的值是大于126的。如果Python试着用默认的ASCII标准来解码这个字节流,它就会报错。我们来看当Python解码这个字符串的时候会发生什么:

>>> x = 'abc' + chr(150)>>> print repr(x)'abc\x96'>>> u'Hello' + xTraceback (most recent call last): File '', line 1, in ?UnicodeDecodeError: 'ASCII' codec can't decode byte 0x96 in position 3: ordinal not in range(128)

我们来用UTF-8来编码另一个字符串:

A UTF-8 encoded string looks like this:[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = 'abc–'[0x61] [0x62] [0x63] [0xe2] [ 0x80] [ 0x93] = 'abc-'

如果你拿起看你熟悉的Unicode编码表,你会发现英文的破折号对应的Unicode编码点为8211(0×2013)。这个值大于ASCII最大值127。大于一个字节能够存储的值。因为8211(0×2013)是两个字节,UTF-8必须利用一些技巧告诉系统存储一个字符需要三个字节。我们再来看当Python准备用默认的ASCII来编码一个里面有字符的值大于126的UTF-8编码字符串。

>>> x = 'abc\xe2\x80\x93'>>> print repr(x)'abc\xe2\x80\x93'>>> u'Hello' + xTraceback (most recent call last): File '', line 1, in ?UnicodeDecodeError: 'ASCII' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)

你可以看到,Python一直是默认使用ASCII编码。当它处理第4个字符的时候,因为它的值为226大于126,所以Python抛出了错误。这就是混合编码所带来的问题。

解码字节流

在一开始学习Python Unicode 的时候,解码这个术语可能会让人很疑惑。你可以把字节流解码成一个Unicode对象,把一个Unicode 对象编码为字节流。

Python需要知道如何将字节流解码为Unicode对象。当你拿到一个字节流,你调用它的“解码方法来从它创建出一个Unicode对象。

你最好是尽早的将字节流解码为Unicode。

>>> x = 'abc\xe2\x80\x93'>>> x = x.decode('utf-8')>>> print type(x)>>> y = 'abc' + chr(150)>>> y = y.decode('windows-1252')>>> print type(y)>>> print x + yabc–abc–

将Unicode编码为字节流

Unicode对象是一个文本的编码不可知论的代表。你不能简单地输出一个Unicode对象。它必须在输出前被变成一个字节串。Python会很适合做这样的工作,尽管Python将Unicode编码为字节流时默认是适用ASCII,这个默认的行为会成为很多让人头疼的问题的原因。

>>> u = u'abc\u2013'>>> print uTraceback (most recent call last): File '', line 1, in UnicodeEncodeError: 'ascii' codec can't encode character u'\u2013' in position 3: ordinal not in range(128)>>> print u.encode('utf-8')abc–

使用codecs模块

codecs模块能在处理字节流的时候提供很大帮助。你可以用定义的编码来打开文件并且你从文件里读取的内容会被自动转化为Unicode对象。

试试这个:

>>> import codecs>>> fh = codecs.open('/tmp/utf-8.txt', 'w', 'utf-8')>>> fh.write(u'\u2013')>>> fh.close()

它所做的就是拿到一个Unicode对象然后将它以utf-8编码写入到文件。你也可以在其他的情况下这么使用它。

试试这个:

当从一个文件读取数据的时候,codecs.open 会创建一个文件对象能够自动将utf-8编码文件转化为一个Unicode对象。

我们接着上面的例子,这次使用urllib流。

>>> stream = urllib.urlopen('http://www.google.com')>>> Reader = codecs.getreader('utf-8')>>> fh = Reader(stream)>>> type(fh.read(1))>>> Reader

单行版本:

>>> fh = codecs.getreader('utf-8')(urllib.urlopen('http://www.google.com'))>>> type(fh.read(1))

你必须对codecs模块十分小心。你传进去的东西必须是一个Unicode对象,否则它会自动将字节流作为ASCII进行解码。

>>> x = 'abc\xe2\x80\x93' # our 'abc-' utf-8 string>>> fh = codecs.open('/tmp/foo.txt', 'w', 'utf-8')>>> fh.write(x)Traceback (most recent call last):File '', line 1, in File '/usr/lib/python2.5/codecs.py', line 638, in write return self.writer.write(data)File '/usr/lib/python2.5/codecs.py', line 303, in write data, consumed = self.encode(object, self.errors)UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 3: ordinal not in range(128)

哎呦我去,Python又开始用ASCII来解码一切了。

将UTF-8字节流切片的问题

因为一个UTF-8编码串是一个字节列表,len( )和切片操作无法正常工作。首先用我们之前用的字符串。

[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = 'abc–'

接下来做以下的:

>>> my_utf8 = 'abc–'>>> print len(my_utf8)6

神马?它看起来是4个字符,但是len的结果说是6。因为len计算的是字节数而不是字符数。

>>> print repr(my_utf8)'abc\xe2\x80\x93'

现在我们来切分这个字符串。

>>> my_utf8[-1] # Get the last char'\x93'

我去,切分结果是最后一字节,不是最后一个字符。

为了正确的切分UTF-8,你最好是解码字节流创建一个Unicode对象。然后就能安全的操作和计数了。

>>> my_unicode = my_utf8.decode('utf-8')>>> print repr(my_unicode)u'abc\u2013'>>> print len(my_unicode)4>>> print my_unicode[-1]–

当Python自动地编码/解码

在一些情况下,当Python自动地使用ASCII进行编码/解码的时候会抛出错误。

第一个案例是当它试着将Unicode和字节串合并在一起的时候。

>>> u'' + u'\u2019'.encode('utf-8')Traceback (most recent call last): File '', line 1, in UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

在合并列表的时候会发生同样的情况。Python在列表里有string和Unicode对象的时候会自动地将字节串解码为Unicode。

>>> ','.join([u'This string\u2019s unicode', u'This string\u2019s utf-8'.encode('utf-8')])Traceback (most recent call last): File '', line 1, in UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 11: ordinal not in range(128)

或者当试着格式化一个字节串的时候:

>>> '%s\n%s' % (u'This string\u2019s unicode', u'This string\u2019s utf-8'.encode('utf-8'),)Traceback (most recent call last): File '', line 1, in UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 11: ordinal not in range(128)

基本上当你把Unicode和字节串混在一起用的时候,就会导致出错。

在这个例子里面,你创建一个utf-8文件,然后往里面添加一些Unicode对象的文本。就会报UnicodeDecodeError错误。

>>> buffer = []>>> fh = open('utf-8-sample.txt')>>> buffer.append(fh.read())>>> fh.close()>>> buffer.append(u'This string\u2019s unicode')>>> print repr(buffer)['This file\xe2\x80\x99s got utf-8 in it\n', u'This string\u2019s unicode']>>> print '\n'.join(buffer)Traceback (most recent call last): File '', line 1, in UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 9: ordinal not in range(128)

你可以使用codecs模块把文件作为Unicode加载来解决这个问题。

>>> import codecs>>> buffer = []>>> fh = open('utf-8-sample.txt', 'r', 'utf-8')>>> buffer.append(fh.read())>>> fh.close()>>> print repr(buffer)[u'This file\u2019s got utf-8 in it\n', u'This string\u2019s unicode']>>> buffer.append(u'This string\u2019s unicode')>>> print '\n'.join(buffer)This file’s got utf-8 in it This string’s unicode

正如你看到的,由codecs.open 创建的流在当数据被读取的时候自动地将比特串转化为Unicode。

最佳实践

1.最先解码,最后编码

2.默认使用utf-8编码

3.使用codecs和Unicode对象来简化处理

最先解码意味着无论何时有字节流输入,需要尽早将输入解码为Unicode。这会防止出现len( )和切分utf-8字节流发生问题。

最后编码意味着只有你打算将文本输出到某个地方时,才把它编码为字节流。这个输出可能是一个文件,一个数据库,一个socket等等。只有在处理完成之后才编码unicode对象。最后编码也意味着,不要让Python为你编码Unicode对象。Python将会使用ASCII编码,你的程序会崩溃。

默认使用UTF-8编码意味着:因为UTF-8可以处理任何Unicode字符,所以你最好用它来替代windows-1252和ASCII。

codecs模块能够让我们在处理诸如文件或socket这样的流的时候能少踩一些坑。如果没有codecs提供的这个工具,你就必须将文件内容读取为字节流,然后将这个字节流解码为Unicode对象。

codecs模块能够让你快速的将字节流转化为Unicode对象,省去很多麻烦。

解释UTF-8

最后的部分是让你能对UTF-8有一个入门的了解,如果你是个超级极客可以无视这一段。

利用UTF-8,任何在127和255之间的字节是特别的。这些字节告诉系统这些字节是多字节序列的一部分。

Our UTF-8 encoded string looks like this:[ 97 ] [ 98 ] [ 99 ] [ 226 ] [ 128 ] [ 147 ] = 'abc–'

最后3字节是一个UTF-8多字节序列。如果你把这三个字节里的第一个转化为2进制可以看到以下的结果:

11100010

前3比特告诉系统它开始了一个3字节序列226,128,147。

那么完整的字节序列。

11100010 10000000 10010011

然后你对三字节序列运用下面的掩码。(详见这里)

1110xxxx 10xxxxxx 10xxxxxxXXXX0010 XX000000 XX010011 Remove the X's0010 000000 010011 Collapse the numbers00100000 00010011 Get Unicode number 0x2013, 8211 The '–'

这里仅仅是关于UTF-8的一些入门的基本知识,如果想知道更多的细节,可以去看UTF-8的维基页面。

python中unicode编码怎么用_详解Python2.x中对Unicode编码的使用相关推荐

  1. 电路中滤波电容和退耦电容_详解电源滤波电路中的高频滤波电容电路

    图2-12所示是电源滤波电路中的高频滤波电路.电路中,一个容量很大的电解电容C1(2200µF)与一个容量很小的电容C2(0.01µF)并联,C2是高频滤波电容,用来进行高频成分的滤波,这种一大一小两 ...

  2. 前端中unescape是什么意思_详解JavaScript中的Unescape()和String() 函数

    JavaScript中的Unescape()和String() 函数详解,具体内容如下所示: 定义和用法 JavaScript unescape() 函数可对通过 escape() 编码的字符串进行解 ...

  3. java调用项目中的文件_详解eclipse项目中.classpath文件的使用

    1 前言 在使用eclipse或者myeclipse进行java项目开发的时候,每个project(工程)下面都会有一个.classpath文件,那么这个文件究竟有什么作用? 2 作用 .classp ...

  4. java按钮权限控制_详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Se ...

  5. jupyter notebook python3路径_详解修改Anaconda中的Jupyter Notebook默认工作路径的三种方式...

    方式1. 打开Windows的cmd,在cmd中输入jupyter notebook --generate-config如下图: 可以看到路径为D:\Users--找到此路径修改jupyter_not ...

  6. mysql临键锁_详解 MySql InnoDB 中的三种行锁(记录锁、间隙锁与临键锁)

    详解 MySql InnoDB 中的三种行锁(记录锁.间隙锁与临键锁) 前言 InnoDB 通过 MVCC 和 NEXT-KEY Locks,解决了在可重复读的事务隔离级别下出现幻读的问题.MVCC  ...

  7. js模板字符串自定义类名_详解JavaScript ES6中的模板字符串

    这篇文章主要介绍了详解JavaScript ES6中的模板字符串,JS的ES6版本带来诸多简洁化方面的重大改进,需要的朋友可以参考下 在 ES6 中引入了一种新的字符串字面量 - 模板字符串,除了使用 ...

  8. idea看java版本设置_详解IntelliJ IDEA 中如何配置多个jdk版本即(1.7和1.8两个jdk都可用)...

    详解IntelliJ IDEA 中如何配置多个jdk版本即(1.7和1.8两个jdk都可用) 有时候需要看Java源码,但是 Java 1.7 和 Java 1.8的差别的关系,有时候你想查看不同jd ...

  9. vue输入框输入触发事件_详解.vue文件中监听input输入事件(oninput)

    详解.vue文件中监听input输入事件(oninput) .vue文件其实是一个组件,关于它的说明我之前也写过一篇文章,地址:.vue文件,今天这篇文章要讲的是.vue文件中监听input的输入值变 ...

最新文章

  1. 2018 Multi-University Training Contest 7
  2. python 引用(import)文件夹下的py文件的方法
  3. Android手机系统adb常用的命令
  4. python sqlite3的使用
  5. (转) android里,addContentView()动态增加view控件,并实现控件的顶部,中间,底部布局...
  6. 矩阵每一行重复_【剑指offer】65 矩阵中的路径
  7. python爬虫之 ---------------- 正则表达式(1)
  8. oracle创建数据库表空间
  9. linux内存测试工具memtest,Linux-内存检测利器Memtest86+v1.70
  10. 大数据“跨界”文化金融
  11. 为什么天才容易患阅读障碍症_王俊凯因“耳石症”缺席跨年晚会,这种病与熬夜玩手机有关?...
  12. 适配器模式之迭代器模式
  13. eclipse tomcat cannot create a server using the...
  14. C3:Unity3D制作智能家居设计软件——绘制户型(二)
  15. 规范化理论:无损分解的测试算法
  16. Boost.Asio的使用技巧
  17. 服装销售系统计算机毕业论文,大学本科计算机专业服装销售管理系统设计毕业论文.doc...
  18. Python collections模块之Counter()详解
  19. 猿创征文 |【算法入门必刷】数据结构-栈(五)
  20. QT(6)-QStandardItemModel

热门文章

  1. 前端 Excel文件导入
  2. 目前的企业管理想要信息化,主要存在哪些问题?
  3. 翻译推理_逆否命题/联言命题/选言命题
  4. RPGツクール相关rtp下载
  5. md0变成了md127
  6. 昨天乔戈里被北京沙尘暴重拳出击,今天已经更新热腾腾的腾讯二三面面经!(带答案)...
  7. Dell笔记本、Win10系统开机时没有密码框的一个解决办法。
  8. 如何用pid控制燃气锅炉的火力_请教燃气锅炉燃烧机的控制
  9. 安卓ListView
  10. cgi_header-unable-to-find-LFLF-问题排查