龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > python编程 >

关于你不想知道的所有Python3 unicode特性

时间:2014-11-29 11:33来源:网络整理 作者:网络 点击:
分享到:
我的读者知道我是一个喜欢痛骂Python3 unicode的人。这次也不例外。我将会告诉你用unicode有多痛苦和为什么我不能闭嘴。我花了两周时间研究Python3,我需要发泄我的失望。在这些责骂中,

我的读者知道我是一个喜欢痛骂Python3 unicode的人。这次也不例外。我将会告诉你用unicode有多痛苦和为什么我不能闭嘴。我花了两周时间研究Python3,我需要发泄我的失望。在这些责骂中,仍然有有用的信息,因为它教我们如何来处理Python3。如果没有被我烦到,就读一读吧。

这次吐槽的内容会不一样。不会关联到WSGI或者HTTP及与其相关的东西。通常,我被告知我应该停止抱怨Python3 Unicode系统,因为我不写别人经常写的代码(HTTP库之类的东西),所以我这次准备写点别的东西:一个命令行应用程序。我写了一个很方便的库叫click来让编写它更加简单。

注意,我做的是每一个新手Python程序员做的事情:写一个命令行应用程序。Hello World程序。但是不同以往,我想要确保应用程序是稳定的并且对于Python2和Python3的Unicode都是支持的,还能够进行单元测试。所以接下来的就是如何来实现它。

我们想做什么

在Python3我们作为开发者需要好好使用Unicode。显然,我觉得这意味着所有的文本数据都是Unicode,所有非文本数据都是字节。在这么美妙的世界里所有的东西只有黑与白,Hello World的例子非常直截了当。所以让我们来写一些shell工具吧。

这是用Python2形式实现的应用程序:

import sys
import shutil
 
for filename in sys.argv[1:]:
  f = sys.stdin
  if filename != '-':
    try:
      f = open(filename, 'rb')
    except IOError as err:
      print >> sys.stderr, 'cat.py: %s: %s' % (filename, err)
      continue
  with f:
    shutil.copyfileobj(f, sys.stdout)

显然,命令在处理任何命令行选项的时候也不是特别好,不过至少能够用。所以我们开始码代码吧。

UNIX里的UNICODE

上面的代码在Python2是不行的,因为你暗中处理字节。命令行参数是字节,文件名是字节,文件内容也是字节。语言卫道士会指出这是不对的,这样会引发问题,但如果你开始更多考虑它,你会发现这是个不固定的问题。

UNIX是字节,已经被定义成了这样,并且一直会是这样。为了理解为什么你需要观察数据传输的不同场景。

  • 终端
  • 命令行参数
  • 操作系统输入输出层
  • 文件系统驱动

顺便提一下,这不是数据可能通过的唯一东西,但是我们来了解一下,在多少场景下我们能了解一个编码。答案是一个也没有。至少我们需要理解一个编码是终端输出区域信息。这个信息可以用来展现转换,也能够理解文本信息所拥有的编码。

举个例子,如果LC_CTYPE的值为en_US.utf-8告诉应用程序系统使用US English,并且大部分文本数据是utf-8编码。实际上还有很多别的变量,不过我们假定这是我们唯一需要看的。注意LC_CTYPE并不代表所有的数据都是utf-8编码的。它代替通知应用程序如何分类文本特性并且什么时候需要应用转换。

这很重要,原因是因为c locale。c locale是POSIX唯一指定的现场,它说所有ASCII编码和来自命令行工具的回复会按照POSIX spec里定义的来对待。

在我们上面的cat工具里,如果它是比特,没有别的方法来对待这些数据。原因是shell里没有指定这数据是什么。例如你调用cat hello.txt,终端会在对应用程序编码的时候对hello.txt进行编码。

但是现在想想这个例子echo *。Shell会把目前目录的所有文件名传递给你的应用程序。那它们是什么编码?文件名没有编码!

UNICODE疯狂

现在一个用Windows的人看到这里会说:弄UNIX的人在搞什么呢。但这还不算悲惨。产生这些工作的原因是一些聪明的人设计得这个系统能够向后兼容。不像Windows把每个API都定义两次,在POSIX上,最好的处理方法是为了显示的目的将其假定为字节,用默认的编码方式来编码。

用上面的cat命令来举例。比如有一个关于文件无法打开的错误信息,原始是因为它们不存在或者它们是受保护的,或者其他任何的原因。我们假定文件是用latin1编码的,因为它是来自1995年外部驱动。终端会获取标准输出,它将会试着把它用utf-8编码,因为这是它认为的编码。因为字符串是latin1编码的,因为它无法顺利得解码。但是不怕,不会有什么崩溃,因为你的终端在无法处理它的时候会无视它。

它在图形界面上怎样?每种有两个版本。在一个像Nautilus 这样的图形界面上列出所有的文件。它把文件名和图标关联起来,能够双击并且试着使文件名能够显示出来,因而把它解码。例如它会尝试用utf-8解码,错误的地方用问题记号来替代。你的文件名可能不是完全可读的但那是你仍能打开文件。

精彩图集

赞助商链接