你所需要的,不仅仅是一个好用的代理。
正确处理文本,特别是正确处理Unicode。是个老生常谈的问题,有时甚至会难倒经验丰富的开发者。并不是因为这个问题很难,而是因为对软件中的文本,开发者没有正确理解一些关键概念及其表示方法。在StackOverflow上搜索关于UnicodeDecodeError相关的问题,可以看到很多人都有这样的误解。这些错误的概念可以追溯到Unicode出现之前。那时许多现今的开发者还没入职,也包括我自己。如果这些错误的概念没有散布开来,其实不是个问题。现在很多人都有这些错误概念,部分原因是因为有些非常流行的语言传播,甚至固化了这些错误概念,使得纠正起来反而变得很困难。
根据对Unicode的支持情况,编程语言可以划分为4类:
那么,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的类型层次中,有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()将默认的编码设置为真正会用到的编码。但这样仅仅是将问题隐藏起来,虽然这样刚开始能解决一些文本处理问题。但缺乏实际可行性,因为许多应用,特别是网络应用,在不同的地方会使用不同的文本编码。
正确的解决方法是修改代码,以正确的方式处理文本。下面是一些应该做到的指导性意见:
顺便说一句,Python 3修复了这些问题,可以正确的处理unicode和字符串,这样Python就完全位于第四类中了,更多信息参见官方的更新说明中关于Unicode的部分。
希望这些内容能帮到你,如果对unicode到底是什么,如何处理unicode有疑惑的话,现在应该都清楚了。下次遇到UnicodeEncodeError或UnicodeDecodeError错误时,就应该完全知道问题出在哪,也知道如何去修复这些问题!