Python和Unicode:一切应该是Unicode

22

如果这是一个很长的问题,请谅解:

我已经用Python编程大约六个月了。自学,从Python教程开始,然后是Stack Overflow,最后只要需要就在Google上搜索。

这里有个悲伤的部分:没有人告诉我所有的字符串都应该是Unicode。不,我不是在撒谎或捏造事实,但是教程在哪里提到了呢?而且我看到的大多数例子都使用字节串而不是Unicode字符串。我正在浏览时看到了这个SO上的问题,它说Python中的每个字符串都应该是Unicode字符串。这几乎让我哭了!

我读到Python 3.0中默认情况下每个字符串都是Unicode,所以我的问题是针对2.x版本的:

  1. 我应该这样做:

    print u'Some text' 还是只需要 print 'Text' ?

  2. 一切都应该是Unicode,这是否意味着,比如说我有一个 tuple:

    t = ('First', 'Second'),它应该是 t = (u'First', u'Second')?

    我读到可以使用 from __future__ import unicode_literals,然后每个字符串都将成为Unicode字符串,但是我是否也应该在容器中这样做?

  3. 读/写文件时,我应该使用 codecs 模块。对吗?还是应该使用标准的读/写方式,在需要时进行编码或解码?

  4. 如果我从 raw_input() 获取字符串,那么我是否也应该将其转换为Unicode?

在2.x中处理上述所有问题的常见方法是什么?是使用 from __future__ import unicode_literals 语句吗?

对不起,我是一个新手,但这改变了我长期以来一直在做的事情,所以显然我很困惑。


1
为什么不使用Python 3并跟上时代的步伐呢? - David Heffernan
17
几乎所有的 Linux 发行版仍然带有 2x。编写 2x 程序并没有错,因为大部分库也都是基于 2x 的。 - Falmarri
3
挑剔一下:from __future__ import unicode_literals,复数形式。 - Karl Knechtel
6
@David:因为Python在3.x的转换中完全搞砸了代码兼容性,并且只提供了一个笨拙、不合理的代码转换方案进行过渡,实际上几乎没有人使用它。似乎他们看了所有语言和API所做的过去转换,并决定Python是特殊的,不需要那样做。但他们错了。 - Glenn Maynard
@David:您是如何得出3.x比2.x更新的结论的? - SamB
显示剩余4条评论
6个回答

13
“始终使用Unicode”建议主要是为了更轻松地过渡到Python 3。如果您的代码中有大量非Unicode字符串访问,则需要更多的工作来进行移植。
此外,您不应该根据情况决定字符串是否应存储为Unicode。您也不应该仅因为更改其内容而更改字符串类型及其语法。
还容易使用错误的字符串类型,导致代码“基本上”可以工作,或者在Linux中可以工作但在Windows中不能,在一个地区可以工作,但在另一个地区却不能。例如,在UTF-8语言环境中执行for c in "漢字"会迭代每个UTF-8字节(总共六个),而不是每个字符;这是否会导致问题取决于您对它们的处理方式。
原则上,如果您使用Unicode字符串,则不应该出现任何问题,但是如果您在不应使用常规字符串时使用常规字符串,则可能会出现问题。
然而,在实践中,在Python 2中到处使用Unicode字符串很麻烦。 codecs.open不能自动选择正确的语言环境;以下代码将失败:
codecs.open("blar.txt", "w").write(u"漢字")

真正的答案是:

import locale, codecs
lang, encoding = locale.getdefaultlocale()
codecs.open("blar.txt", "w", encoding).write(u"漢字")

从stackoverflow获取而来的内容是:...这很繁琐,迫使人们编写辅助函数才能打开文件。codecs.open应该在未指定编码时自动使用locale中的编码;Python未能使如此简单的操作方便是人们通常不会完全使用Unicode的原因之一。

最后,需要注意的是,在某些情况下,Unicode字符串在Windows中更为关键。例如,如果您处于西方区域设置并且有一个名为“漢字”的文件,则必须使用Unicode字符串访问它,例如os.stat(u"漢字")。无法使用非Unicode字符串访问它;它将无法看到该文件。

因此,原则上我认为Unicode字符串建议是合理的,但要注意我自己通常甚至不遵循它。


1
你解决了很多问题,所以加一分。特别是区域设置部分很有帮助。 - user225312
2
@Phillipp:当然可以;我没有义务为此跳过障碍,特别是当它只影响一小部分用户时。如果他们想要从我的软件获得支持,他们可以付钱给我(或我的雇主)来跳过这些障碍,或者游说Python修复这个问题;与UTF-8相比,广泛编码的整个重点就在于不必处理这个问题。每一段代码,如s[0:20] + '...'都会受到影响;这是Python允许这种模糊、难以测试的不一致性进入语言的错,而不是坚定地标准化UCS4。只有在必要时才会担心它。 - Glenn Maynard
2
“始终使用Unicode”建议主要是为了更轻松地过渡到Python 3,这是不正确的。一切都应该使用Unicode,因为您的程序并不是在1970年代的美国运行。 - Marcin
@glennmaynard:实际上,你错了。这只是指定文件的源编码,并不改变语言行为。尝试阅读实际的参考资料,而不是你依赖的任何来源。 - Marcin
@Marcin 文件的源编码就是Python 2.x中非u""字符串的编码,因此如果文件是UTF-8,则需指定编码:utf-8并使用'print "漢字"'打印UTF-8字符串。(你们两个都对和傲慢,这是一种糟糕的组合,所以我不会再回复这个讨论。) - Glenn Maynard
显示剩余9条评论

10

不是每个字符串都应该是Unicode。在你的Python代码中,你知道字符串文字是否需要成为Unicode,因此将每个字符串文字变成Unicode文字没有任何意义。

但是有一些情况下,你应该使用Unicode。例如,如果你有任意输入的文本,请使用Unicode。你迟早会遇到一个非美国人使用它,并且他想像他平常写的那样书写文本。除非你的输入和输出恰好使用相同的编码,否则你会遇到问题,而这一点是无法确定的。

总之,字符串不应该是Unicode,文本应该是Unicode。但是结果因人而异。

具体来说:

  1. 这里不需要使用Unicode。你知道这个字符串是否为ASCII。

  2. 取决于是否需要将这些字符串与Unicode合并。

  3. 两种方式都可以。但不要在必要时进行编码解码。尽早解码,尽可能晚地编码。使用编解码器很好(或从Python 2.7开始使用io)。

  4. 是的。


1
一个有趣的回答,但我有一个疑问:您提到strings不应该是Unicode,而text应该是。这有什么区别? - user225312
@A A 我相信他指的是在控制台、浏览器、保存到文件、数据库等中显示的文本字符串。 - moinudin
@A A: 文本与数据阅读此链接:http://docs.python.org/release/3.0.1/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit - mouad
1
@A A:所谓“文本”,是指“非二进制数据”。Unicode 可以包含文本,但不包括二进制数据;str 可以包含二进制数据和编码文本,str 和 unicode 都是字符串的形式。 - Lennart Regebro
你的回答非常棒,但我选择了Glenn的回答,因为那对大家都有帮助。谢谢! - user225312

5
我个人认为(根据我的简单规则):
  1. 我应该使用:print u '一些文本' 或只是打印'文本' ?

  2. 所有东西都应该是Unicode,这是否意味着,比如说我有一个元组: t = ('First', 'Second'),它应该是t =(u'First',u'Second')

好的,仅当我有一些ASCII 128以上的字符时才使用Unicode文字。

   print 'New York', u'São Paulo'
   t = ('New York', u'São Paulo')
  1. 读写文件时,我应该使用codecs模块,对吗?还是应该使用标准的读写方式,在必要时进行编码或解码?

如果你期望得到Unicode文本,使用codecs模块。

  1. 如果我从raw_input()获取字符串,我是否也应将其转换为Unicode?

只有当你期望得到的Unicode文本可能会在具有不同默认编码(包括数据库)的另一个系统中传输时才需要转换。

编辑(关于混合使用Unicode和字节串):

>>> print 'New York', 'to', u'São Paulo'
New York to São Paulo
>>> print 'New York' + ' to ' + u'São Paulo'
New York to São Paulo
>>> print "Côte d'Azur" + ' to ' + u'São Paulo'
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: 
     ordinal not in range(128)
>>> print "Côte d'Azur".decode('utf-8') + ' to ' + u'São Paulo'
Côte d'Azur to São Paulo

如果将包含utf-8(或其他非ascii字符)的字节字符串与未明确转换的Unicode文本混合使用,您将会遇到麻烦,因为默认情况下假定为ASCII。相反的方式似乎是安全的。如果遵循将包含非ascii字符的每个字符串编写为Unicode文字的规则,则应该可以正常工作。
免责声明:我住在巴西,人们说葡萄牙语,这是一种具有许多非ascii字符的语言。我的默认编码始终设置为“utf-8”。在英语/ ascii系统中可能会有所不同。

你完美地回答了第二个问题。 - user225312
1
如果你将包含utf-8(或其他非ascii字符)的字节字符串与未经明确转换的Unicode文本混合使用,那么你会遇到麻烦,因为默认情况下假定为ASCII。 - Paulo Scardine
1
Unicode虽然有些麻烦,但比旧的“扩展ASCII”和“代码页”混乱要好得多。 - Paulo Scardine
1
@A A:我居住的城市名称中有一个非ASCII字符,自从20年前开始接触计算机以来,我就一直面临这个问题。祝贺你提出了这个好问题。 - Paulo Scardine
1
@Paulo:Unicode 不是痛点。Python2 的 Unicode 是痛点,Windows 的 Unicode 也是痛点。这不是 Unicode 的错。 - Glenn Maynard
显示剩余3条评论

3

这里只是我个人的看法。虽然不如其他答案长而详细,但或许也能有所帮助。

print u'Some text' 或者只用 print 'Text'

我确实更喜欢第一个。如果您知道只有 Unicode 字符串,那么您就有了一个不变量。其他各种语言(如C、C++、Perl、PHP、Ruby、Lua等)有时会因为代码单元序列和整数序列之间缺乏分离而遇到痛苦的问题。我认为在.NET、Java、Python等中严格区分它们的方法要干净得多。

一切都应该是Unicode,这意味着,比如说我有一个tuple:

t = ('First', 'Second'),它应该是 t = (u'First', u'Second') 吗?

是的。

我读到可以使用 from __future__ import unicode_literals 然后每个字符串都将成为 Unicode 字符串,但是我也应该在容器中使用它吗?

是的。未来语句仅适用于它们被使用的文件,因此您可以使用它们而不会干扰其他模块。我通常在 Python 2.x 模块中导入所有 futures,以便更轻松地过渡到 3.x。

读/写文件时,我应该使用 codecs 模块。对吗?还是我只需使用标准的读/写方式,在需要时进行编码或解码?

您应该使用 codecs 模块,因为这样可以避免(或至少很难)意外将不同编码的表示写入单个文件。这也是打开文本模式文件时 Python 3.x 的工作方式。

如果我从 raw_input() 获取字符串,我也应该将其转换为 Unicode 吗?

我认为这也是可以的:在大多数情况下,只处理一种编码更容易,因此我建议尽早将其转换为Python Unicode 字符串。

在2.x中处理上述所有问题的通用方法是什么?使用 from __future__ import unicode_literals 语句吗?

我不知道通用方法是什么,但我一直使用这个语句。我只遇到了非常少的与此方法有关的问题,其中大部分与外部库中的错误相关,即NumPy有时需要字节字符串而没有记录。


2
你在接触 Unicode 之前写了6个月的 Python 代码,这意味着 Python 2.x 中字符串的 ASCII 默认并没有给你带来任何问题。对于初学者来说,理解 Unicode/码点/编码本身就是一个难以解决的问题;因此,大多数教程自然会跳过它,直到你更加掌握基础知识为止。这就是为什么在像《Dive Into Python》这样的书中,Unicode 只在后面的章节中提到。
如果你需要在应用程序中支持 Unicode,我建议参考Kumar McMillan 在 PyCon 2008 上关于 Unicode 的最佳实践清单。它应该能回答你剩下的问题。

3
并没有给他造成问题,因为初学者程序员通常不会实现国际化等功能,并且不会关心如果发布一个不支持Unicode的程序会给其他人带来什么问题,特别是在Windows系统中。 - Glenn Maynard

-2

1/2) 就我个人而言,我从未听说过“始终使用Unicode”。这对我来说似乎相当愚蠢。我猜如果你计划支持需要Unicode支持的其他语言,那么我可以理解。但除此之外,我不会这样做,因为它似乎比它值得的更麻烦。

3) 我只会按标准方式读写,并在必要时进行编码。


2
我在我的问题中发布的链接基础上,建议“始终使用Unicode”。如果你在谷歌上搜索“Python Unicode”,你会发现同样的建议。 - user225312

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接