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 混淆?的主要内容,如果未能解决你的问题,请参考以下文章
用ofstream/ifstream 读写Unicod的TXT