跳转至

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 开发、爬虫、数据可视化实战。