第4章–文本和字节序列
本章将讨论下述话题:
• 字符、码位和字节表述
• bytes、bytearray 和 memoryview 等二进制序列的独特特性
• 全部 Unicode 和陈旧字符集的编解码器
• 避免和处理编码错误
• 处理文本文件的最佳实践
• 默认编码的陷阱和标准 I/O 的问题
• 规范化 Unicode 文本,进行安全的比较
• 规范化、大小写折叠和暴力移除音调符号的实用函数
• 使用 locale 模块和 PyUCA 库正确地排序 Unicode 文本
• Unicode 数据库中的字符元数据
• 能处理字符串和字节序列的双模式 API
4.1 4.2 4.3 字符问题、字节概要 基本的编解码器
主要讲了
python2、python3 的str、unicode、bytes之间的差异
编码解码的概念
memoryview、struct模块对字节序列的处理
对这一块不太感冒,只做一个python3的str、unicode、bytes的笔记
python3 的str其实就是unicode字符,交互界面看到的是人类的字符,但其实存储中是unicode码位(code point 四位十六进制的数)。
unicode其实就是做了一个人类所有字符码位的一个一一映射关系表
数据的传输永远都是二进制的,我们要传输unicode字符,就需要把码位转换成二进制数据,这就另外规定了很多不同的编码方式--utf-8 utf-16 GBK IOS-2312等,有的编码方式支持所有unicode码位到二进制的转换,比如utf系列,但有的只支持部门unicode码位,比如GBK!
python用bytes跟bytearray来表示二进制的字节,每个元素是介于0-255(2^8)之间的整数,即每个元素代表一个字节(8bits)
str.encode(encoding='')用于把str编码成bytes类型
bytes.decode(encoding='')用于把bytes类型解码成str类型
4.4了解编解码问题
编解码相关的异常种类为UnicodeEncodeError和UnicodeDecodeError
处理UnicodeEncodeError
有的编码方式只能把部分unicode字符转换成字节序列,如果目标编码中没有定义某个字符,就会跑出UnicodeEncodeError异常
str.encode()函数里的errors参数规定了出现编码错误后程序的处理方式,默认为strict,直接抛UnicodeencodeError,但还有其他方式如ignore、replace、xmlcharrefreplace等
处理UnicodeDecodeError
并非所有字节序列都能转换成unicode字符,如果转换失败,就会抛UnicodeDecodeError,bytes.decode()函数同样有errors参数,参数类型跟str.encode的相同。
有的旧编码比如’cp1252’ ‘iso8859_1’ 'koi8_r’能够解码任何字节序列而不抛出错误,但会得到错误的字符
判断字节序列的编码
如果只拿到字节序列,我们不能百分百猜出它是用什么编码格式编码的,是utf-8还是gbk,不清楚
往往通信的时候需要规定跟说明编码格式,比如http跟xml都需要在头部说明编码方式
如果没有给定编码方式,就只能靠猜测了,Chardet模块就是用来做编码格式猜测的
BOM用于判断大端还是小端
用utf-16把字符写入文本时,往往会在开头写入额外的两个字节,这两个字节就是BOM,如果是FFFE 那么就是小端,如果是FEFF,那么就是大端,下面的例子中是FFFE,打印b[2]=97是’a’字符的低字节序,b[3]=0是’a’字符的高字节序,说明使用小端模式
>>> a='abc'
>>> b=a.encode('utf-16')
>>> b
b'\xff\xfea\x00b\x00c\x00'
>>> b[0]
255
>>> b[1]
254
>>> b[2]
97
>>> b[3]
0
UTF-16有两个变种:UTF-16LE UTF-16BE 显式指定小端模式和显式指定大端模式,使用者两个变种不会产生BOM
4.5 处理文本文件
两句话,输入输出时把字节序列解码成unicode,处理过程中全程用unicode。
输入输出指定编码方式,不要使用默认编码,特别是window系统。
4.6规范化Unicode字符串
Unicode有组合字符–变音符号和附加到前一个字符上的记号,打印时作为一个整体
正常来说s1 s2虽然码位不同,但表示出来是一样的,程序应该判定两者一致的,但python不会,为了避免这种问题,要用unicodedata.normalize 函数提供的 Unicode 规范化,函数的第一个参数是下面四个之一[“NFC”,“NFD”,“NFKC”,“NFKD”]
NFC(Normalization Form C)使用最少的码位构成等价的字符串
NFD 把组合字符分解成基字符和单独的组合字符
使用NFC时,有的单字符会被转换成另一个单字符,这类字符是视觉上相同但符号位不同的字符
NFKC、NFKD在NFC、NFD的基础上再考虑一些兼容字符
使用 NFKC 和 NFKD 规范化形式时要小心,而且只能在特殊情况中使用,例
如搜索和索引,而不能用于持久存储,因为这两种转换会导致数据损失
4.6.1 大小写折叠
把unicode文本变成小写,标准函数是str.casefold() str.lower()功能类似,但有少数例外,两者对unicode处理结果不同的有116个码位
可以根据项目要求做unicode字符的自定义规范化,比如去掉变音符号,把一些拉丁文字符映射成英文等
4.7 Unicode文本排序
略
4.8 Unicode数据库
unicodedata库不仅包括码位与字符名称之间的映射,还有各个字符的元数据,以及字符之间的关系。例如,unicodedata记录了字符是否可以打印、是不是字母、是不是数字,或者是不是其他数值符号。字符串的 isidentifier、isprintable、isdecimal 和 isnumeric 等方法就是靠这些信息作判断的
4.9 支持字符串和字节序列的双模式API
标准库中有的函数支持接收字符串或者字节序列作为参数,比如re os中的一些函数
-
re库
-
os库
os模块所有函数中文件名或路径名参数都可以是字符串或者字节序列
为了方便手动处理字符串或者字节序列的文件名或者路径名,os提供特殊的编解码函数
fsencode(filename)不管filename是字符串还是字节序列,最终返回字节序列
fsdecode(filename)不管filename是字符串还是字节序列,最终返回字符串
unix平台上这两个函数用surrogateescape错误处理方式,避免遇到意外字节序列时卡住,window用strict
surrogateescape把无法理解的字节序列替换成Unicode 中 U+DC00 到 U+DCFF 之
间的码位(Unicode 标准把这些码位称为“Low Surrogate Area”),这些码位是保留的,
没有分配字符,供应用程序内部使用。编码时,这些码位会转换成被替换的字节值