其实我感觉这方面的知识用得不会很多,所以大概是介绍不了太多文字了(

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/24⁵(4 和上标 5)会被拆分为普通字符 45(这种已经完全改变原义了),但是这样很方便用户搜索。

Unicode 三明治原则

接下来是 Unicode 的一个重要处理原则:Unicode 三明治原则

这个原则应该能解决很多 Unicode 相关的问题,它的要求是:

  • 在接收字符时解码(bytesstr
  • 中间程序对字符的操作都是 100% 的 str
  • 发送字符时再编码(strbytes

这是一个相当合理的操作,能有效减少一些出问题的可能性。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,这会导致一些潜在的问题。