Python / Django 中的 Unicode 与 UTF-8 混淆?

Posted

技术标签:

【中文标题】Python / Django 中的 Unicode 与 UTF-8 混淆?【英文标题】:Unicode vs UTF-8 confusion in Python / Django? 【发布时间】:2010-09-06 12:49:28 【问题描述】:

我在Django tutorial中偶然发现了这段话:

Django 模型有一个默认的 str() 方法,该方法调用 unicode() 并将结果转换为 UTF-8 字节串。这意味着 unicode(p) 将返回一个 Unicode 字符串,而 str(p) 将返回一个普通字符串,字符编码为 UTF-8。

现在,我很困惑,因为 afaik Unicode 不是任何特定的表示形式,那么 Python 中的“Unicode 字符串”是什么?这是否意味着UCS-2?谷歌搜索出现了this "Python Unicode Tutorial",它大胆地指出

Unicode 是一种双字节编码,涵盖了世界上所有常见的书写系统。

这是完全错误的,是吗?我曾多次被字符集和编码问题弄糊涂,但在这里我很确定我正在阅读的文档很困惑。当 Python 给我一个“Unicode 字符串”时,有人知道它发生了什么吗?

【问题讨论】:

【参考方案1】:

什么是 Python 中的“Unicode 字符串”?这是否意味着 UCS-2?

Python 中的 Unicode 字符串在内部存储为 UCS-2(固定长度 16 位表示,与 UTF-16 几乎相同)或 UCS-4/UTF-32(固定长度 32 位表示)。这是一个编译时选项;在 Windows 上,它始终是 UTF-16,而许多 Linux 发行版为其 Python 版本设置了 UTF-32(“宽模式”)。

您通常不应该关心:您会将 Unicode 代码点视为字符串中的单个元素,并且您不知道它们是存储为两个字节还是四个字节。如果您在 UTF-16 构建中并且需要处理基本多语言平面之外的字符,那么您将做错,但这仍然非常罕见,真正需要额外字符的用户应该编译宽版本。

大错特错,是吗?

是的,这是完全错误的。公平地说,我认为该教程相当陈旧。它可能早于广泛的 Unicode 字符串,如果不是 Unicode 3.1(在基本多语言平面之外引入字符的版本)。

Windows 习惯使用术语“Unicode”来表示 NT 内部使用的 UTF-16LE 编码,还有一个额外的混淆来源。来自 Microsoftland 的人可能经常会复制这种具有误导性的习惯。

【讨论】:

我认为 UCS-2 和 UTF-16 之间的区别至少值得注意,因为一个是固定长度的,另一个不是。如果我完全关心内部表示,我想知道。 真的是UCS-2吗?由于 Python 可能会处理字符 > sys.maxunicode,因此您可能会碰巧在中间对字符进行切片。使用 UCS-2,如何显示/存储/编码/解码sys.maxunicode 以上的字符? (使用 Python 3.1 测试) 它必须是 UTF-16,因为 UCS-2 不支持代理对。演示 Python 3.1 的狭窄构建,在代理中分解一个字符:list(chr(sys.maxunicode + 1))。结果是['\ud800', '\udc00']。有人可以在(狭义的)Python 2 上确认这一点吗? 是的,Python2 还允许通过unichr\U00nnnnnn 字符串文字转义将单个非BMP 字符创建为两个代理代码单元。所以从技术上讲,它使用的是带有 UCS-2 语义的 UTF-16。不过,我讨厌使用“UTF-16”这个术语,因为它可能意味着一系列 16 位代码单元,或者相同的基于大端或小端字节的编码,这会导致整个负载的混乱。实际上,所有“UCS-2”实际上都是“UTF-16”,因为后者是前者更常用的超集。 窄 Unicode 构建中 Python Unicode 字符串的长度是 UTF-16 代码单元的数量,而不是实际的 Unicode 代码点。通过任意索引进行截断和其他切片选项确实可以将代理对分成两半,结果是一些丢失/替换的字符。在狭窄的构建中,unichr(0x10345) 根本失败; len(u'\U00010345')2。这是您为与 Win32 UTF-16LE API 轻松交互而付出的代价。大多数其他环境使用 UCS-4,它不会遇到任何此类问题。【参考方案2】:

同时,我进行了细致的研究来验证 Python 的内部表示是什么,以及它的限制是什么。 “The Truth About Unicode In Python”是一篇非常好的文章,直接来自 Python 开发者。显然,内部表示是 UCS-2 或 UCS-4,具体取决于编译时开关。所以 Jon,它不是 UTF-16,但无论如何你的回答让我走上了正轨,谢谢。

【讨论】:

【参考方案3】:

Python 将 Unicode 存储为 UTF-16。 str() 将返回 UTF-16 字符串的 UTF-8 表示形式。

【讨论】:

Python 将 Unicode 字符串存储为 UTF-16 或 UTF-32,具体取决于平台和编译选项。 str(unicode_string) 在什么平台上返回 UTF-8?你试过了吗?例如str(u"\u0369") 这两点都错了。 str(unicode_val) 将根据sys.getdefaultencoding() 进行编码。【参考方案4】:

来自Wikipedia on UTF-8:

UTF-8(8 位 UCS/Unicode 转换格式)是一种用于 Unicode 的可变长度字符编码。它能够表示 Unicode 标准中的任何字符,但 UTF-8 的字节码和字符分配的初始编码向后兼容 ASCII。由于这些原因,它逐渐成为电子邮件、网页[1] 和其他存储或流式传输字符的地方的首选编码。

因此,它介于 1 到 4 个字节之间,具体取决于您希望在 Unicode 领域内表示的字符。

From Wikipedia on Unicode:

在计算领域,Unicode 是一种行业标准,允许计算机一致地表示和操作以世界上大多数书写系统 表达的文本。

因此它能够代表世界上大多数(但不是全部)的书写系统。

我希望这会有所帮助:)

【讨论】:

【参考方案5】:

那么什么是“Unicode 字符串” 蟒蛇?

Python“知道”你的字符串是 Unicode。因此,如果您对其进行正则表达式,它将知道哪个是字符,哪个不是等等,这真的很有帮助。如果你做了一个 strlen 它也会给出正确的结果。例如,如果您对 Hello 进行了字符串计数,您将得到 5(即使它是 Unicode)。但是,如果您对一个外来词进行了字符串计数,并且该字符串不是 Unicode 字符串,那么您将得到更大的结果。 Pythong 使用 Unicode 字符数据库中的信息来识别 Unicode 字符串中的每个字符。希望有帮助。

【讨论】:

以上是关于Python / Django 中的 Unicode 与 UTF-8 混淆?的主要内容,如果未能解决你的问题,请参考以下文章

使用demjson解析unicod

快速理解python2中的编码问题

用ofstream/ifstream 读写Unicod的TXT

字符编码:ASCII,Unicod和UTF-8

编码(ACSII unicod UTF-8)QT输出中文乱码深入分析

老男孩python学习笔记一