《Fluent Python》学习笔记:第 4 章 文本和字节序列

本文主要是 Fluent Python 第 4 章的学习笔记。这部分主要是介绍了 Unicode 字符串、二进制序列、二者之间转换时使用的编码、处理编码错误、处理文本文件、 Unicode 规范化比较等。

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()。

这部分有待日后需要时,进行深入研究。

巨人的肩膀

  1. 《Fluent Python》
  2. 《流畅的 Python》

后记:
我从本硕药学零基础转行计算机,自学路上,走过很多弯路,也庆幸自己喜欢记笔记,把知识点进行总结,帮助自己成功实现转行。
2020下半年进入职场,深感自己的不足,所以2021年给自己定了个计划,每日学一技,日积月累,厚积薄发。
如果你想和我一起交流学习,欢迎大家关注我的微信公众号每日学一技,扫描下方二维码或者搜索每日学一技关注。
这个公众号主要是分享和记录自己每日的技术学习,不定期整理子类分享,主要涉及 C – > Python – > Java,计算机基础知识,机器学习,职场技能等,简单说就是一句话,成长的见证!
每日学一技

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值