跳转至

8.1 文件与目录——二进制数据

内容梗概

本节课是"文件与目录"一章的开篇,承接上一节文本文件读写的输入输出内容,转入二进制数据的学习。老师先介绍了本章的整体安排(二进制文件操作、BMP 解析、字符集与编码、目录操作),随后重点讲解了 Python 中表示二进制数据的两种类型:bytes 和 bytearray。课程详细解释了 bytes 字面值的显示规则(可打印 ASCII 字符与 \x 十六进制形式混合出现),并通过多个实例演示了二进制数据的创建、切片、hex()/fromhex() 转换和 index() 查找等序列操作。

知识点详解

本章内容安排(约 00:00 - 01:00)

  • 上一节已讲过文本文件的读取与写入;本节重点转向二进制文件,将解析上节课用到的 BMP 文件。
  • 顺便会讲字符集与编码问题(计算机领域绕不过去的问题),以及用 Python 遍历目录、重命名文件等目录操作(如批量整理照片)。
  • Python 常被称为"脚本语言",遍历目录是最常见的辅助性需求之一;Windows / Linux / macOS 上的目录操作方式一致。

二进制数据的两种类型:bytes 与 bytearray(约 01:00 - 04:15)

  • 之前讲数据类型基础和进阶时回避了二进制数据,放到文件这一节结合讲解,逻辑递进更顺畅。
  • Python 中二进制数据有两种类型:
  • bytes:字节(复数),不可变序列,绝大多数情况下使用的类型。
  • bytearray:可变序列,类比"元组 : 列表 = bytes : bytearray"的对应关系;少数需要手动修改二进制数据内容时才用到。

bytes 字面值的显示规则(约 04:16 - 15:30)

  • print 一个 bytes 对象时,显示为 b'...' 的形式,小写 b 表示 Binary,这就是该类型的**字面值**。
  • 字面值以字节为单位,每个符号表示一个字节。字面值中混合存在两种形式:
  • 可打印字符:字节的值能翻译成可打印的 ASCII 字符(含 \n、\t 等可转义字符)时,直接显示该字符。例如字符 0 对应的字节显示为 0。
  • \x 十六进制形式:无法翻译成可打印字符时,直接以 \x 加该字节的十六进制值显示,如 \x00。\x 后面两个十六进制字符共表示一个字节(1 个十六进制字符 = 4 比特,2 个 = 8 比特)。
  • 为什么用十六进制而不用二进制/八进制表示字节(约 07:42):
  • 纯二进制一个字节要写 8 个 0/1,太啰嗦;
  • 八进制一个字符表示 3 个比特,8 比特除不尽 3,无法整齐对齐一个字节;
  • 十六进制每 4 比特正好一个字符(0–F),两个字符合一个字节,表示简洁、对齐方便。因此所有计算机课程、WinHex 等工具都用十六进制显示二进制数据。
  • 示例辨析:a = b'\x00\x00\x00' 与 b = b'000' 都是三个字节但**值不同**——前者每个字节十六进制为 0x00,后者每个字节是字符 0 的 ASCII 码 0x30。可通过 b == c 比较验证。
  • 提问互动(约 16:11):a 含三个字符 0、一个 \x00、一个 \n(换行,十六进制 0A),共 5 个字节;换行是可转义字符,打印时显示为 \n 而不是 \x0a。
  • 常见可转义字符对应的值:\n(换行,0x0A)、\t(制表符/Tab,0x09)、空格(0x20)。

bytes 的创建方式(约 19:47 - 22:25)

  • 直接用字面值赋值:a = b'\x00\x00\x00'。
  • 内置函数 bytes() 转换:
  • 传入整数 n:创建包含 n 个全 0 字节的序列,如 bytes(3) 得到 b'\x00\x00\x00'。
  • 传入可迭代对象:如 bytes(range(3)) 得到 b'\x00\x01\x02'。
  • bytearray 不能用字面值直接创建(b'123' 的类型永远是 bytes),需要用 bytearray(b'...') 包裹字面值来构造;打印时会显示 bytearray(b'...') 以区分类型。
  • 实际很少用 bytearray:处理二进制数据大多是从文件读进来分析,或构造 bytes 直接写回文件,很少需要在内存中逐字节修改。

二进制数据的序列操作(约 24:04 - 31:30)

  • bytes / bytearray 都是序列,之前学的序列操作(索引、切片、index 查找等)全部可用。
  • 切片:如 bytes(range(10))[:5] 得到 b'\x00\x01\x02\x03\x04'。
  • .hex() 方法(约 25:41):把二进制数据转成**字符串**,内容是其十六进制表示,如 b.hex() 得到 '0001020304'。
  • bytes.fromhex(s)(约 26:30):反向操作,接收十六进制字符串生成 bytes。
  • 字符串长度必须是 2 的整数倍(每两个字符一个字节),否则报 ValueError;
  • 字符只能是 0-9、A-F(十六进制字符),打 H、中文等都会报错。
  • index():查找某个值在二进制序列中的下标,与之前做 Base64 编码练习时查字符索引的用法一样。
  • 顺带提及:之前"直男表白"的例子用 hex/fromhex 会更方便,用 ord/chr 函数也可以。

示例与演示

  1. 定义 a = b'\x00\x00\x00',print(a) 显示字面值、print(type(a)) 显示 <class 'bytes'>。
  2. 比较 b'\x00\x00\x00' 与 b'000' 是否相等(False),b'000' 与 b'\x30\x30\x30' 是否相等(True),说明字符 0 对应 ASCII 码 0x30。
  3. 含 5 个字节的混合字面值示例:三个字符 0 + \x00 + \n,展示可打印字符与 \x 形式混合出现的效果。
  4. bytes(3) 创建 3 字节全 0 序列;bytes(range(3)) 创建 b'\x00\x01\x02'。
  5. bytearray(b'123') 创建可变序列,演示其与 bytes 打印形式的差异。
  6. 序列操作综合示例:bytes(range(10)) 切片取前 5 字节;b.hex() 转十六进制字符串 '0001020304';bytes.fromhex('2030ff') 中 0x20 显示为空格、0x30 显示为 0、0xFF 显示为 \xff;index 查找 0x00 的位置(索引 0)。
  7. 错误演示:fromhex 传长度为奇数的串、含 H 等非法十六进制字符的串,均抛 ValueError。

重点与难点

  • bytes 字面值的两种混合形式:可打印 ASCII 字符(含 \n、\t 等转义字符)直接显示,不可打印字节显示为 \x + 两位十六进制。看到一串字面值时必须能数清到底有几个字节,这是本节反复强调的意识。
  • \x00(值为 0 的字节)与字符 '0'(值为 0x30)完全不同,不能混淆。
  • 十六进制是表示二进制数据的标准形式:一个字节 = 8 比特 = 两个十六进制字符。
  • bytes 不可变、bytearray 可变;字面值 b'...' 创建出来的永远是 bytes。
  • fromhex() 的参数长度必须是 2 的整数倍且只含合法十六进制字符。

关联内容

  • 承接上一节(第 7 章)文本文件的读写与 BMP 转 ASCII 艺术练习;本节的二进制知识是下一节"二进制文件分析(BMP 解析)"的直接基础。
  • 与"数据类型基础/进阶"中序列操作(索引、切片、index)、字典等知识衔接;ord/chr 函数在早期字符与数字转换练习中已出现。
  • 提到上学期使用 WinHex 做文件恢复、查看硬盘数据的十六进制显示,与本节的十六进制表示一脉相承。
  • 字符编码问题为 8.4 节"字符集与编码"埋下伏笔。