
Python中的Unicode处理
其实我感觉这方面的知识用得不会很多,所以大概是介绍不了太多文字了(
Python 2 与 Python 3 的字符串差异
首先是 Python 3 的 str,它其实就是 Python 2 的 unicode 类(我记得是完全一样的),而 Python 2 的 str 则和 Python 3 的 bytes 类似(不完全一样)。所以 Python 3 对字符的支持可能更多、更好。不过鉴于 Python 3 的内部灵活表示机制(PEP 393),也可能存在一些需要注意的细节,比如纯 ASCII 字符串内部使用 1 字节存储,一旦出现更大的码点字符,整个字符串的内部存储单元会相应扩大。
Unicode 码点组合与规格化
关于 Unicode,其实有很多字符是可以通过码点组合而来的,虽然显示都一样,但是 Python 解释器可不吃你这套,使用 == 照样为 False。但这就有一个问题了:如果要实现对字符的搜索,这样难道不会搜索不到吗?
所以,我们有了规格化操作,有 4 种方式,分别为:
- NFC:将可以合并的码点合并
- NFD:将能拆分的码点拆分
- NFKC:兼容性合并
- NFKD:兼容性拆分
它们都能起到一个不错的规格化作用(其实 NFC 最常用),但是还是不够适合搜索。于是我们有了 NFKC 和 NFKD(不过可能只适合搜索就是了,因为会改变一些原字符含义),它们会将一些字符拆分,比如 ½ 会被拆分为 1/2,4⁵(4 和上标 5)会被拆分为普通字符 45(这种已经完全改变原义了),但是这样很方便用户搜索。
Unicode 三明治原则
接下来是 Unicode 的一个重要处理原则:Unicode 三明治原则。
这个原则应该能解决很多 Unicode 相关的问题,它的要求是:
- 在接收字符时解码(
bytes→str) - 中间程序对字符的操作都是 100% 的
str - 发送字符时再编码(
str→bytes)
这是一个相当合理的操作,能有效减少一些出问题的可能性。open() 函数就遵循了这样的原则,它会在读取文件时获取 bytes 内容再解码为指定编码内容,写入时则是将需要写入的 str 转为 bytes 再写入。
\N{} 转义字符
Python 还支持一种与 Unicode 相关的特殊转义字符 \N{},它会将 {} 内的 Unicode 名字变为对应的 Unicode 字符,有些时候可能挺方便的。
1 | print("\N{GREEK SMALL LETTER ALPHA}") # 输出: α |
BOM(字节顺序标记)
还有一些编码的文件,你去看它的 bytes 会发现其中有些原本"不存在"的字符,其中 BOM 就是一个很有用的标记。它代表文件所使用的编码是哪个,比如 UTF-16(带 BOM)就有 BOM 来区分 UTF-16LE 和 UTF-16BE,这样 BOM 就很重要了。不过也有不带 BOM 的一些编码格式(带上 BOM 其实也没区别),比如 UTF-8。但是鉴于一些文档软件会自动给编码生成 BOM,所以解码时建议使用 utf-8-sig,有没有 BOM 它都能正常解码。
注意:千万不要给 Python 代码文件的 UTF-8 加上 BOM,这会导致一些潜在的问题。



