2023-04 Base64 讲解(程序结构作业课)¶
内容梗概¶
本课是一次线下作业课,分为两部分。开头约 5 分钟老师介绍了本学期后续课程的整体安排:剩余 11 节课中,6 节课留给 Web 开发、爬虫、数据可视化三个实战模块(各两周),"函数与类"和"数据类型进阶"两章不再线下重复、要求自行看视频,其余每周对应第 6~9 章各一节。主体内容围绕 Base64 作业展开:从名字来源(base 进制概念)讲起,解释 Base64 编码的背景动机(基于文本的传输协议无法直接传二进制数据),手工演示了 "Man" 的完整编码过程(含补零和 = 填充),随后在 VS Code 中带着大家从零手写了一个 Base64 编码函数,最后布置了作业要求。课的末尾把剩余时间留给学生自己练习。
知识点详解¶
1. 课程进度安排说明(约 00:00 - 04:40)¶
- 本学期还剩 11 节课:今天讲程序结构的 Base64 作业;后续 4 节分别对应输入输出文件、目录、模块、包与第三方库;最后 3 个实战模块(Web 开发、爬虫、数据可视化)各占两周(共 6 节)。
- 第 5 章(数据类型进阶)和第 11 章附近的"函数与类"内容有视频且后续课程中会反复用到,不再线下重复,需自行安排看视频。
- 第 6、7、8、9 章每周线下对应一章,老师会做演示、实验和练习。
- 数据可视化部分会涉及 pandas;老师提到 Office 365 已内置 Python 并与 pandas 结合。
- 本课程定位是"引子":只给初步概念、带做最基本操作,不必焦虑两周能否学完实战内容。
2. Base64 名字的含义(约 06:50 - 10:55)¶
base64= 英文单词 base + 数字 64,即以 64 为 base(基数)的算法。- 类比进制概念:十进制 base 为 10,二进制 base 为 2。
- 引出 Python 内置函数
int():int("16")默认按 base=10 转换;int("1101010", base=2)或等价写法把二进制字符串转为十进制数(课堂演示结果为 106)。int()的第二个参数名就叫base,返回值总是十进制。
3. Base64 的核心功能与符号表(约 10:55 - 12:43)¶
- Base64 本质上**不是数字、不表示数字,而是表示数据**:核心功能是把任意数据转换为**可打印文本**(printable text),编码结果中不会出现不可见字符。
- 64 个符号的构成:
- 大写字母
A-Z:26 个 - 小写字母
a-z:26 个(累计 52) - 数字
0-9:10 个(累计 62) +和/(正斜杠,即 Linux 路径分隔符方向的那个):2 个(累计 64)
4. 为什么需要 Base64:基于文本的传输协议(约 12:43 - 19:10)¶
- 互联网很多**协议是基于文本的**,最有名的是 HTTP(超文本传输协议):浏览器发出的 request 和服务器返回的 response 都是人类可读的文本(课堂用浏览器 F12 打开百度页面,在 Network 面板查看请求头演示)。
- 但 HTTP 要传"超文本"内容:音乐、图像、视频等多媒体数据含大量不可打印字节。典型问题是
0x00:C 语言中\0表示字符串结束,HTTP 协议同样用 00 标志请求结束。 - 举例:RGB 表示像素时 R、G、B 各取 0~255,黑色像素
000000(十六进制)的三个分量全为 0,一张图里黑色像素比比皆是,直接按文本传输会立刻出错。 - 除 HTTP 外还有 SMTP(发邮件的协议),同样基于文本(都以 TP 结尾,文本传输协议);邮件附件(图片、甚至钓鱼邮件里的可执行程序)必然包含
0x00字节,因此急需一种方法把任意二进制数据统一翻译成可打印字符——Base64 应运而生。
5. Base64 编码算法的手工推演(约 19:10 - 28:10)¶
- 基本思路:1 个字节 = 8 个 bit,表示范围 0~255;把范围缩小到 0~63,对应的二进制宽度是 6 bit(6 bit 最大为 63)。因此把原始字节流展开成二进制后,**每 6 个 bit 一组**重新分组。
- 课堂用 Python 逐步演示(以
"Man"为例): ord('m')→ 77,bin(77)→0b1001101,去掉0b前缀并在前面补零凑足 8 bit:01001101。- 同理把
a、n各自转成 8 bit 串。 - 三个字节拼成 24 bit 序列,按 6 bit 一组切成 4 组。
- 每组用
int(串, base=2)转成十进制数(课堂演示得到 19、22、5、46)。 - 把这组数字当作**下标**到 64 字符表(A-Z、a-z、0-9、+、/)中查字符:19→T、22→W、5→F、46→u,即
"Man"的 Base64 编码为TWFu(字幕中口述为 "TWFU",按算法应为TWFu)。 - 长度除不尽时的处理(截断与填充)(约 26:25 - 28:10):
- 3 字节 = 24 bit 恰好被 6 整除;若字节数不是 3 的倍数,最后一组不足 6 bit 时**在后面补 0** 凑足 6 bit,再转字符。
- 之后还需做 padding 补齐:不写字母、直接用等号
=补足长度。剩 4 bit(即少 1 个字节)时补 1 个=;剩 2 bit(少 2 个字节)时补 4 个 0 并补 2 个=。 - 实用提示:打 CTF 时看到一串莫名其妙的字符以
=结尾,可以盲猜是 Base64,丢进解码器试试。
6. 手写 Base64 编码函数的代码要点(约 28:10 - 53:12)¶
- 文件命名陷阱:文件不能叫
base64.py,因为 Python 标准库已有同名模块base64,会产生命名空间冲突(import 时不知道指谁),要刻意错开命名。 - 作业**不允许直接
import base64调库**(老师演示用 Copilot 自动补全一行import base64后明确否定);作业目的是练熟**列表、切片、循环**等控制语句。真实场景(如打 CTF)当然直接用现成工具。 - 编码函数的实现步骤(课堂上在 VS Code 中边写边测):
- 定义函数,参数名取
data(可接收任何数据,甚至直接读入的二进制文件)。 - 遍历:
for b in data:——若data是二进制序列,则每次迭代把一个字节放入b(隐含知识点)。注意byte是关键词/易冲突名,循环变量改用b。 - 转二进制串:
ord()可把字符或字节转成 int(其输入可以是 string、byte、byte array),再套bin()得到二进制字符串。 - 处理
bin()结果的两个问题:- 有
0b前缀 → 用切片[2:]去掉(从下标 2 开始取到末尾); - 不一定够 8 位 → 前面补零:朴素写法
while len(temp) < 8: temp = "0" + temp(0 要补在前面)。
- 有
- 养成**写一段测一段**的习惯:每一步打印中间结果验证(老师演示中差点因比较符少写一个
=、变量名写成b而非temp翻车)。 - 拼接:定义空串
before_regroup,循环中before_regroup += temp,把所有 8 bit 串连成一个整体。 - 重新分组:
for i in range(0, len(before_regroup), 6):,用group = before_regroup[i:i+6]切片取 6 bit 一组(注意字符串切片用方括号、冒号,不是函数调用)。 - 转下标:
index = int(group, 2)(把 6 bit 二进制串转成数字,作为字符表索引)。 - 构造 64 字符表(约 44:16 - 50:48):
- 方法一:列表推导式 +
chr()/ord()。ord('A')是 65,用[chr(x) for x in range(65, 65+26)]生成 A-Z;列表推导式形式为[表达式 for 变量 in 旧列表]。 - 简化写法:直接
range(ord('A'), ord('A')+26)。 - 复习
ord()与chr():ord()字符→数字;chr()数字→对应 ASCII 字符。 - 拼完整表:A-Z 列表 += a-z 列表 += 0-9 列表 +=
['+', '/'](课堂通过复制终端结果逐段+=完成)。
- 方法一:列表推导式 +
- 查表并拼接结果:
encoded = base64表[index],encoded_string += encoded,循环结束后return encoded_string。 - 老师故意留的坑:课堂代码**没有处理字节数非 3 倍数时的补零与
=padding**,也**没有写解码过程**——这两部分是作业要完成的内容。
7. 其他零散知识点¶
- Windows 终端退出 Python 交互模式(
>>>):Ctrl+C退不出去,用Ctrl+Z回车(约 28:42)。 - 老师提到可安装 Anaconda 以使用 Jupyter Notebook 演示,但自己电脑没装,本课直接在 VS Code 里写。
- 老师课前误删了作业 PPT(更新仓库时覆盖掉了),本课靠白板+视频+维基百科的 Base64 示意图现场讲。
示例与演示¶
int()与 base 演示:终端中int("16")→ 16;int("1101010", base=2)→ 106,说明 base 参数语义与"返回值总是十进制"。- HTTP 协议基于文本的演示:浏览器访问百度,F12 → Network 面板,查看最上方请求的文本形式请求头。
- 手工编码
"Man"→TWFu:完整走了一遍ord→bin→ 补零凑 8 bit → 拼接 24 bit → 6 bit 分组 →int(x, 2)转下标 → 查表得字符的流程。 - 截断/填充推演:以
"Ma"(2 字节)为例演示剩余 4 bit 补零转字符后补 1 个=;以单字节为例演示剩余 2 bit 补 4 个 0 后补 2 个=。 - VS Code 现场编码:从零写出 Base64 编码函数(步骤见知识点详解第 6 节),每完成一步立即运行打印验证(以
"Man"为测试输入)。 - 作业要求:
- 基于课堂代码继续完成:补齐"长度非 3 的倍数时补零 +
=padding"的逻辑,并自行实现 Base64 **解码**过程。 - 不允许
import base64直接调库(作业目的是练列表、切片、循环)。 - 提交方式:不需要新建仓库;把之前的作业仓库克隆下来(若本地还有就更方便),新建分支
0x02,在该分支上写代码并 push。还没绑定邮箱/收不到验证码的同学可先在本地写好,等信息化处发验证码后再提交。
重点与难点¶
- Base64 是"数据→可打印文本"的编码,不是数字进制、更不是加密:名字虽来自进制概念,但它表示的是数据。
- 8 bit → 6 bit 的重新分组是算法核心:6 bit 恰好表示 0~63,对应 64 个可打印符号;老师口误把"六个比特"说成"三个比特"后当场纠正,需注意(3 bit 只有 8 种取值)。
bin()结果的两个坑:自带0b前缀需切片[2:]去掉;不足 8 位要在**前面**补零("0" + temp,不是temp + "0")。- 补零与
=padding 是本作业的留白:课堂代码只处理了整除情况,作业必须自己实现非整除时的补零、padding 及解码。 - 文件命名避开标准库同名模块:自己的脚本不要叫
base64.py,否则 import 冲突。 - 禁止直接调库:作业目的是练列表、切片、循环控制语句;
import base64一行解决不算完成作业。 - 细节易错点:切片用
[i:i+6](冒号,非函数调用的逗号);比较运算符是==不是=;range(0, len(s), 6)的步长为 6。
关联内容¶
- 前置知识:进制概念(十进制/二进制/base);C 语言中
\0字符串结束符;Python 内置函数int(x, base)、ord()、chr()、bin();字符串切片、range()步长、列表推导式、循环与累加拼接——都是此前章节(数据类型、程序结构)的内容,本课作业正是为了练熟它们。 - 与后续章节的关联:HTTP、SMTP 等基于文本的协议在本课只作背景介绍,HTTP request/response 的结构会在后续 Web 开发 与 爬虫 实战模块中深入使用;二进制文件的处理与后续 输入输出文件 章节相关。
- 与工程实践的关联:作业通过 Git 分支(
0x02)提交,延续此前课程建立的仓库+分支工作流;Base64 在 CTF 竞赛中常见(看到=结尾的字符串可盲猜 Base64)。 - 课程安排关联:本课说明第 5 章(数据类型进阶)与函数/类章节只在线上看视频,第 6~9 章每周线下对应一章,最后三周为 Web 开发、爬虫、数据可视化实战。