正确处理文本,特别是正确处理Unicode。是个老生常谈的问题,有时甚至会难倒经验丰富的开发者。并不是因为这个问题很难,而是因为对软件中的文本,开发者没有正确理解一些关键概念及其表示方法。在StackOverflow上搜索关于UnicodeDecodeError相 关的问题,可以看到很多人都有这样的误解。这些错误的概念可以追溯到Unicode出现之前。那时许多现今的开发者还没入职,也包括我自己。如果这些错误 的概念没有散布开来,其实不是个问题。现在很多人都有这些错误概念,部分原因是因为有些非常流行的语言传播,甚至固化了这些错误概念,使得纠正起来反而变 得很困难。

根据对Unicode的支持情况,编程语言可以划分为4类:

  • 在Unicode出现或流行之前编写的语言。C和C++就属于这一类。这类语言对unicode的支持参差不齐。或没有内置到语言中,或很难正确的使用。因此开发者常常会用错。
  • 对Unicode支持稍好一点。这些语言在Unicode广泛流行后才出现的,但语言中对unicode的操作方式是严重错误的。虽然这些语言诞生较晚,但依然含有第一类语言中的所有缺点。以我的经验,其中代表语言就是PHP。尽管还有其他语言也同样糟糕。
  • 对Unicode支持基本正确,但有少数致命缺点的语言。这一类语言比较“现代”,且能理解Unicode,但依然无法让开发者正确的处理 unicode,导致在这些语言中对unicode会出现一些严重不足。让我很沮丧的是,Python 2.x就属于这一类(下文会详细介绍)。
  • 能正确处理Unicode的语言。这些语言完全支持Unicode,可以用Unicode方便快速的完成任务,且不易出错。Java和.NET平台就属于这一类语言。

那么,Unicode到底是什么,我们在Unicode上犯了哪些错误?Joel这篇The absolute minimum every software developer absolutely, positively must know about unicode绝对是每个软件开发者必须阅读的文章。为了为简洁起见,以及照顾那些天生耐心不够的朋友,我会在本文中对其进行总结。

字符和字节

基本事实是,若想正确的处理文本,就必须了解字符的抽象概念。不严谨的定义一下,字符表示的是文本中的单个符号。更重要的是,一个字符不是一个字 节。我再强调一遍!一个字符不是一个字节!!!而且,一个字符有许多表示方法,不同的表示方法会使用不同的字节数。就像前面我说的那样,字符就是文本中最 小的单元。

Unicode以大家都认可的方式定义了一系列的字符。可以将Unicode理解成一个字符数据库,每个字符都与唯一的数字关联,称为code point。这样,英文大写字母A的codepoint是U+0041。而欧元符号的codepoint是U+20A0,其他类似。一个文本字符串就是这样一系列的codepoint,表示字符串中每个字符元素。

当然,你迟早会需要储存和传输这些理论上的Unicode字符串。如果选择一种其他人可以理解的方式以字节方式进行表示,就可以以大家都理解的方式互相发送文本。这里就需要引入字符编码(encoding)。

字符编码是在理想的字符和实际的字节表示方法之间的映射。这种映射无需面面俱到,即在某种编码中也许无法表示一些特定的字符。同时也无须为每个字符使用相同的内存空间,譬如某些字符使用单字节编码,而其他字符需要多个字节。

由于同一个字符的字节表现形式不止一种。这意味着当遇到了一串字节,如果不知道使用的是什么编码,即使知道这些字节表示的是文本,也不知道是什么意 思。所能做的就是猜使用的编码。简而言之,字节不是文本。即使忘了文中介绍的所有内容,也要记住这句话。为了读写文本,归根结底就是要知道其中使用的编码 方式,不管是从约定、标识信息、或是其他方法得知。

Python是如何处理Unicode

从这里开始介绍Python的Unicode支持。在Python的类型层次中,有3种不同的字符串类型:“unicode”,表示Unicode 字符串(文本字符串)、“str”,表示字节字符串(二进制数据);“basestring”。表示前两种字符串类型的父类。在我看来,Python在这 里犯了一个错误,根据前面的定义,这让Python成为第三类语言,而没有成为第四类。

我用了很长的篇幅苦口婆心的强调字节和字符在本质上是不同的东西,只有通过字符编码才能互相转换。但不幸的是,Python犯了两个互不相关的错误,轻轻松松的就会让你忘掉这些。

第一个错误的严重性值得商榷:即将一串字节视为字符串。是否应该这样做还有争议。Java和,NET认为这样做是不对的,而其他一些语言却持有相反 的态度。无论如何,你可能希望对文本进行某些操作,如正则匹配、字符串替代等。将这些操作应用到字节序列上都是没有意义的。而Python将字节序列作为 另一种类型的字符串对待,允许在这两者上执行同样的操作。

第二个错误的严重性大一些,Python试图在字节串和字符串之间以不为人所察觉的方式进行转化。在不同的转换中,在条件允许的情况 下,Python会试图在字节串和unicode字符串直接进行转换。例如将字节串和unicode字节串连接到一起时。根据前面的介绍,不使用 encoding就在不同类型之间进行转换是没有意义的。所以Python依赖一个“默认编码”,该编码由sys.setdefaultencoding()指定。在大多数平台上,默认的是ASCII编码。但对于所有转换,使用这种编码几乎都是错误的。如果不手动指定编码就调用str()unicode(),或是函数以字符串作为参数,但传递的是其他类型的参数时,都会使用这个默认编码。

走出这个unicode困境的一个解决办法是,调用sys.setdefaultencoding()将默认的编码设置为真正会用到的编码。但这样仅仅是将问题隐藏起来,虽然这样刚开始能解决一些文本处理问题。但缺乏实际可行性,因为许多应用,特别是网络应用,在不同的地方会使用不同的文本编码。

正确的解决方法是修改代码,以正确的方式处理文本。下面是一些应该做到的指导性意见:

  • 所有文本字符串都应该是unicode类型,而不是str类型。如果处理的是文本,而变量类型是str,这就是bug了!
  • 若要将字节串解码成字符串,需要使用正确的解码,即var.decode(encoding)(如,var.decode('utf-8'))。将文本字符串编码成字节,使用var.encode(encoding)。
  • 永远不要对unicode字符串使用str(),也不要在不指定编码的情况下就对字节串使用unicode()
  • 当应用从外部读取数据时,应将其视为字节串,即str类型的,接着调用.decode()将其解释成文本。同样,在将文本发送到外部时,总是对文本调用.encode()
  • 如果代码中使用字符串字面值来表示文本,总是应该含有’u'前缀。但实际上,永远不要在代码中定义原始的字符串字面值。不管怎样,我自己是很讨厌这一条,也许其他人也和我一样吧。

顺便说一句,Python 3修复了这些问题,可以正确的处理unicode和字符串,这样Python就完全位于第四类中了,更多信息参见官方的更新说明中关于Unicode的部分。

希望这些内容能帮到你,如果对unicode到底是什么,如何处理unicode有疑惑的话,现在应该都清楚了。下次遇到UnicodeEncodeErrorUnicodeDecodeError错误时,就应该完全知道问题出在哪,也知道如何去修复这些问题!

Python-正确使用Unicode的更多相关文章

  1. 在Python中正确使用Unicode

    正确处理文本,特别是正确处理Unicode.是个老生常谈的问题,有时甚至会难倒经验丰富的开发者.并不是因为这个问题很难,而是因为对软件中的文本,开发者没有正确理解一些关键概念及其表示方法.在Stack ...

  2. 【整理】Python中实际上已经得到了正确的Unicode或某种编码的字符,但是看起来或打印出来却是乱码

    转自:http://www.crifan.com/python_already_got_correct_encoding_string_but_seems_print_messy_code/ [背景] ...

  3. python print输出unicode字符

    命令行提示符下,python print输出unicode字符时出现以下 UnicodeEncodeError: 'gbk' codec can't encode character '\u30fb ...

  4. JavaScript如何正确处理Unicode编码问题!

    原文:JavaScript 如何正确处理 Unicode 编码问题! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. JavaScript 处理 Unicode 的方式至少可以说是令人 ...

  5. 关于python中的unicode字符串的使用

    基于python2.7中的字符串: unicode-->编码encode('utf-8')-->写入文件 读出文件-->解码decode('utf-8')-->unicode ...

  6. Python编码和Unicode

    原文链接: ERIC MORITZ   翻译: 伯乐在线- 贱圣OMG译文链接: http://blog.jobbole.com/50345/ 我确定有很多关于Unicode和Python的说明,但为 ...

  7. Python字符串编码——Unicode

    ASCII码 我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte).也就是 ...

  8. Python标准模块--Unicode

    1 模块简介 Python 3中最大的变化之一就是删除了Unicode类型.在Python 2中,有str类型和unicode类型,例如, Python 2.7.6 (default, Oct 26 ...

  9. python 中的unicode详解

    通过例子来看问题是比较容易懂的. 首先来看,下面这个是我新建的一个txt文件,名字叫做ivan_utf8.txt,然后里面随便编辑了一些东西. 然后来用控制台打开这个文件,同样也是截图: 这里就是简单 ...

  10. python爬虫得到unicode编码处理方式

    在用python做爬虫的时候经常会与到结果中包含unicode编码,需要将结果转化为中文,处理方式如下 str.encode('utf-8').decode('unicode_escape')

随机推荐

  1. response (响应对象)

    一.response响应过程 在去发送一个请求时, 会找到tomcat引擎 引擎会找到对应的web应用,并且会创建request对象和response对象 找到应用后, 会执行应用的web.xml再去 ...

  2. vijos1889:天真的因数分解

    题目链接 vijos1889:天真的因数分解 题解 同bzoj2440: [中山市选2011]完全平方数 就是改成了求有平方因子数,依旧考虑二分,只是把容斥系数取一下相反数,也就是把莫比乌斯函数求一个 ...

  3. [洛谷P3809]【模板】后缀排序

    [洛谷P3809][模板]后缀排序 题目大意: 对于给定的长度为\(n(n\le10^6)\)的字符串求后缀数组\(sa[i]\). 思路: 倍增+快排构造后缀数组.代码参考<挑战程序设计竞赛& ...

  4. Java程序运行时内存划分

    1.Java程序跨平台运行的原因 主要原因是:各种平台的JVM和字节码文件 Java源程序--具体平台的机器代码文件---被编译器翻译成平台无关的Class文件,又用特定JVM运行字节码文件,JVM在 ...

  5. Ubuntu 16.04通过NetworkManager(GUI)配置网桥

    说明:配置好网桥之后一定要重启,不然不生效.这个是Desktop版GUI设置的问题.Server版不会. 配置: 参考: http://www.jb51.net/LINUXjishu/333778.h ...

  6. Java并发包之闭锁/栅栏/信号量

    二.同步工具类详解 1.Semaphore信号量:跟锁机制存在一定的相似性,semaphore也是一种锁机制,所不同的是,reentrantLock是只允许一个线程获得锁,而信号量持有多个许可(per ...

  7. Android 卡顿优化 3 布局优化 工具 Hierarchy Viewer

    欲善其事, 先利其器. 分析布局, 就不得不用到Hierarchy Viewer了. 本文工具使用皆以GithubApp的详情界面RepoDetailActivity为例说明. 为了不影响阅读体验, ...

  8. EasyUI datagrid 双击行事件

    1.EasyUI 实现点击行的任何位置,触发事件 onLoadSuccess:function(data){ } , pagination : false, //双击事件 onDblClickRow: ...

  9. window进入本地组策略编辑器和计算机管理界面

    win+R 进入运行窗口 输入gpedit.msc 进入本地组策略编辑器, 右键我的电脑,点击管理 进入计算机管理界面

  10. unity reflection probe --- forward deferred transparent opaque

    deferred 和forward reflection probe 的差别 deferred ref是逐像素的 forward是逐 obj 但我还遇到一个问题就是box projection ref ...