在Python中正确使用Unicode

正确处理文本,特别是正确处理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错误时,就应该完全知道问题出在哪,也知道如何去修复这些问题!

时间: 2024-10-16 22:59:47

在Python中正确使用Unicode的相关文章

乱码问题引申 python 中string和unicode

HtmlTestRunner的乱码问题 1生成的报告中,对print打印的数据都记录下来,但是数据有些会存在乱码.如下面.有些又没有乱码. 这到底是怎么回事呢? str=t.encode('utf-8') print str 第一个test我以utf-8编码,看来htmlTestRunner不是utf-8 编码. 为何第二个正确了呢? 第二个是unicode编码方式. 也就是说,可以被其他任何encode了. 原码中已这个进行编码,也就是说他设置为latin-1这种编码方式了.估计是作者自己国家

python中,ascii,unicode,utf8,gbk之间的关系梳理

在计算机中,经常遇到编码问题,本节主要梳理下ascii,unicode,utf8,gbk 这几种编码之间的关系. ASCII 计算机中,所有数据都以0和1来表示.在一开始的时候,要表示的内容比较少,人们使用了ascii编码的方式来编码. ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言,其最多只能用 8 位来表示(一个字节),即:2**8 -

Unicode 范围以及python中生成所有Unicode的方法

Unicode范围和表示语言 Unicode是一个通用的字符集,包含了65535个字符.计算机在处理特殊字符(除了ASCII表以外的所有字符)时都是把Unicode按照一种编码来保存的.当然了,unicode的统一花了不少人的精力,而且不同编码到今天还有一些不兼容的问题,不过平常的代码中了解一些基础也就够了. Unicode字符表示语言的范围参考下文: http://www.cnblogs.com/chenwenbiao/archive/2011/08/17/2142718.html 中文(包括

使用C语言为python编写动态模块(1)--从底层深度解析python中的对象以及变量

楔子 我们知道可以通过使用C语言编写动态链接库的方式来给python加速,但是方式是通过ctypes来加载,通过类CDLL将动态链接库加载进来得到一个对象之后,通过这个对象来调用动态链接库里面的函数.那么问题来了,我们可不可以使用C语言为python编写模块呢?然后在使用的时候不使用ctypes加载动态库的方式,而是通过python的关键字import进行加载. 答案是可以的,我们知道可以通过编写py文件的方式来得到一个模块,那么也可以使用C语言来编写C源文件,然后再通过python解释器进行编

python基础篇----字符串unicode

python中处理中文常要用到unicode,因为较容易遇到字符串编码的问题,我一般都是将字符串统一转成unicode去处理 python中定义一个unicode字符串,可以在字符串前面加u: str=u"hello world" python中定义不转义的字符串,可以在字符串前面加r: path=r"c:\programfile\test" 解码将其他字符串格式转为unicode: ret=str.decode("gb2312") ret=st

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

转自:http://www.crifan.com/python_already_got_correct_encoding_string_but_seems_print_messy_code/ [背景] Python中的字符编码,其实的确有点复杂. 再加上,不同的开发环境和工具中,显示的逻辑和效果又不太相同,尤其是,中文的,初级用户,最常遇到的: (1)在Python自带的IDE:IDLE中折腾中文字符,结果看到的差不多都是乱码类的东西,比如:’\xd6\xd0\xce\xc4′ (2)将一个中文

Python中Unicode码和非Unicode码引起的错误与格式转换

1.1. 问题 Problem You need to deal with data that doesn't fit in the ASCII character set. 你需要处理不适合用ASCII字符集表示的数据. 1.2. 解决 Solution Unicode strings can be encoded in plain strings in a variety of ways, according to whichever encoding you choose: Unicode

[转]Python中的str与unicode处理方法

早上被python的编码搞得抓耳挠腮,在搜资料的时候感觉这篇博文很不错,所以收藏在此. python2.x中处理中文,是一件头疼的事情.网上写这方面的文章,测次不齐,而且都会有点错误,所以在这里打算自己总结一篇文章. 我也会在以后学习中,不断的修改此篇博客. 这里假设读者已有与编码相关的基础知识,本文不再再次介绍,包括什么是utf-8,什么是unicode,它们之间有什么关系. str与字节码 首先,我们完全不谈unicode. s = "人生苦短" s是个字符串,它本身存储的就是字节

Python中的str与unicode处理方法

Python中的str与unicode处理方法 2015/03/25 · 基础知识 · 3 评论· Python 分享到:42 原文出处: liuaiqi627 的博客 python2.x中处理中文,是一件头疼的事情.网上写这方面的文章,测次不齐,而且都会有点错误,所以在这里打算自己总结一篇文章. 我也会在以后学习中,不断的修改此篇博客. 这里假设读者已有与编码相关的基础知识,本文不再再次介绍,包括什么是utf-8,什么是unicode,它们之间有什么关系. str与字节码 首先,我们完全不谈u