本文主要是 Fluent Python 第 4 章的学习笔记。这部分主要是介绍了 Unicode 字符串、二进制序列、二者之间转换时使用的编码、处理编码错误、处理文本文件、 Unicode 规范化比较等。
《Fluent Python》学习笔记:第 4 章 文本和字节序列
4.1 字符问题
字符(character)的最佳定义是 Unicode 字符。
码位(code point):即字符的标识(identity),是 0-1 114 111 的数字(十进制),在 Unicode 标准中以 4-6 个十六进制数字表示,并且加前缀“U+”。如:A 的码位是 U+0041。
字符的具体表述取决于编码(encoding)。编码是码位和字节序列(byte sequences)之间转换时使用的算法。在 UTF-8 编码中,A(U+0041)的码位编程当个字节 \x41
。
把码位转换成字节序列的过程是编码(encoding);把字节序列转换成码位的过程是解码(decoding)。换而言之,就是把字节序列变成人类可读的文本字符串就是解码,而把字符串变为用于存储或者传输的字节序列就是编码。
# 编码和解码
s = 'cafÉ'
print(len(s)) # 'cafe' 字符串有4个Unicode字符
b = s.encode('utf-8') # 使用 UTF-8 把 str 对象编码成 bytes对象
print(b)
print(len(b)) # 字节序列 b 有5个字节(在 UTF-8 中“É”编码为2个字节)
c = b.decode('utf-8') # 使用 UTF-8 把 bytes 对象解码成 str 对象
print(c)
4
b'caf\xc3\x89'
5
cafÉ
4.2 字节概要
Python 3 中引入不可变的 bytes 类型和 Python 2.6 添加的可变类型 bytearray 类型。bytes 或 bytearray 对象的各个元素是介于 0-255(含)之间的整数。注意:二进制序列的切片始终是同一类型的二进制序列,包括长度为 1 的切片。
# 包含 5 个字节的bytes和bytearray对象
cafe = bytes('cafÉ', encoding='utf_8') # bytes 对象可以从 str 对象使用给定的编码构建
print(cafe)
print(cafe[0]) # 各个元素是 range(256)内的整数
print(cafe[:1]) # bytes对象的切片还是bytes对象,即使是只有一个字节的切片
cafe_arr = bytearray(cafe)
print(cafe_arr) # bytearray对象没有字面量句法,而是以bytearray()和字节序列字面量参数的形式显示。
print(cafe_arr[-1:]) # bytearray对象的切片还是bytearray对象
b'caf\xc3\x89'
99
b'c'
bytearray(b'caf\xc3\x89')
bytearray(b'\x89')
4.3 基本的编解码器
latin1(即 iso8859_1):一种重要的编码,是其他编码的基础,如 cp1252 和 Unicode。
gb2312:用于编码简体中文的陈旧标准,亚洲语言中使用较广泛的多字节编码之一。
utf-8:目前 Web 最常见的 8 位编码,与 ASCII 兼容。
utf-16le:UTF-16 的 16 位编码方案的一种形式,所有 UTF-16 支持通过转移序列表示超过 U+FFFF 的码位。
4.4 理解编码问题
出现于 Unicode 有关的错误是,首先要明确异常类型。是 UnicodeEncodeError(把字符串转换成二进制序列时)、UnicodeDecodeError(把二进制序列转换成字符串时),还是 SyntaxError。
处理 UnicodeEncodeError 可以通过 errors 参数对错误进行特殊处理。
- errors=‘ignore’ 会跳过无法编码的字符,这不是一个好的选择。
- errors=‘replace’ 把无法编码的字符替换成‘?’;数据损坏了,但是用户知道出了问题。
print('el'.encode('utf_16'))
b'\xff\xfee\x00l\x00'
3.5 处理文本文件
处理文本的最佳实践是“Unicode 三明治”:尽早把输入(如读取文件时)的字节序列解码成字符串,三明治中的“肉片”是程序的业务逻辑,这里只能处理字符串对象,在其他处理过程中一定不能编码或解码,对输出来说,则要尽可能晚地把字符串编码成字节序列。
print(open('cafe.txt', 'w', encoding='utf_8').write('cafÉ'))
print(open('cafe.txt').read())
print(open('cafe.txt', encoding='utf_8').read())
4
caf脡
cafÉ
所以需要在多态设备或多种场合下运行的代码,一定不能依赖默认编码
,打开文件时始终应该明确传入 encoding=参数。
不要在二进制模式中打开文本文件。想判断编码,推荐使用 Chardet 模块,避免反复造轮子。
# default_encodings.py
import sys, locale
expressions = """
locale.getpreferredencoding()
type(my_file)
my_file.encoding
sys.stdout.isatty()
sys.stdout.encoding
sys.stdin.isatty()
sys.stdin.encoding
sys.stderr.isatty
sys.stderr.encoding
sys.getdefaultencoding()
sys.getfilesystemencoding()
"""
my_file = open('dummy', 'w')
for expression in expressions.split():
value = eval(expression)
print(expression.rjust(30), '->', repr(value))
locale.getpreferredencoding() -> 'cp936'
type(my_file) -> <class '_io.TextIOWrapper'>
my_file.encoding -> 'cp936'
sys.stdout.isatty() -> False
sys.stdout.encoding -> 'UTF-8'
sys.stdin.isatty() -> False
sys.stdin.encoding -> 'cp936'
sys.stderr.isatty -> <built-in method isatty of OutStream object at 0x000002DD0692BE88>
sys.stderr.encoding -> 'UTF-8'
sys.getdefaultencoding() -> 'utf-8'
sys.getfilesystemencoding() -> 'utf-8'
4.6 为了正确比较而规范化 Unicode 字符串
使用 unicodedata.normalize 函数提供的 Unicode 规范化。详见 P99。对于大多数应用而言,NFC 是最好的规范化形式,不区分大小写的比较应该使用 str.casefold()。
这部分有待日后需要时,进行深入研究。
巨人的肩膀
- 《Fluent Python》
- 《流畅的 Python》
后记:
我从本硕药学零基础转行计算机,自学路上,走过很多弯路,也庆幸自己喜欢记笔记,把知识点进行总结,帮助自己成功实现转行。
2020下半年进入职场,深感自己的不足,所以2021年给自己定了个计划,每日学一技,日积月累,厚积薄发。
如果你想和我一起交流学习,欢迎大家关注我的微信公众号每日学一技
,扫描下方二维码或者搜索每日学一技
关注。
这个公众号主要是分享和记录自己每日的技术学习,不定期整理子类分享,主要涉及 C – > Python – > Java,计算机基础知识,机器学习,职场技能等,简单说就是一句话,成长的见证!