2023-05 Git讲解、输入输出与ASCII图¶
内容梗概¶
本课分两部分。前半部分(约 0:00–39:00)老师重新系统地讲解了 Git 的核心机制:仓库、远程/本地、分支、提交、推送之间的关系,重点用图示解释了为什么会出现推送冲突(conflict)以及如何解决,并介绍了团队协作的典型分支工作流和 Pull Request 审核机制。后半部分(约 39:00–1:09)回到 Python,快速回顾了输入输出章节(input、字符串格式化、文件打开模式),随后完整演示了本章练习:读取一张 BMP 图片,将像素灰度值映射为不同密度的 ASCII 字符,生成"字符画"文本文件,其中涉及 BMP 文件头结构和 struct.unpack 二进制解析。文件目录章节的作业本课暂未布置,待新仓库建好后在群里通知。
知识点详解¶
一、Git 深入讲解¶
- 可视化工具与分支图(约 1:00):推荐用 TortoiseGit(小乌龟)查看分支状态——右键 → TortoiseGit → Revision Graph(汉化后为"图形化展示"之类),可看到
origin/0x01、main、HEAD等符号。 - 学习网站推荐(约 1:25):Learn Git Branching(learngitbranching),一个可视化的 Git 学习网站,带关卡练习,左边是模拟终端、右边实时显示分支状态,老师已发到群里。
- 基本概念(约 7:40 / 2:40 起):
- 仓库 repo:在 GitLab 上创建仓库时,正常情况下会自动有一个
main默认分支(本学期的仓库因权限问题——非 owner/maintainer——没能创建主分支,属异常情况)。 main与master:早期默认分支叫master,后因政治正确运动改为main,两者看到都算正常。- 分支本质上就是给某个 commit 起的名字(指针);HEAD(课上画了个星号)表示"当前所在位置"。
- remote / local:不在本地的东西叫 remote(远程,服务器上),本地的东西叫 local。
- clone 与 origin(约 10:50):克隆是把远端仓库**完全**搬到本地一份;克隆时默认给远端起的名字叫
origin。所以git push origin main的含义是:推送到 origin 这个远端的 main 分支(也可以是0x01等其他分支名)。 - commit 只影响本地(约 12:15):commit 是本地操作,commit 的编号是一串哈希值(课上简写为 0x00、0x01)。本地 commit 后分支指针(如 main)自动后移指向最新提交,但**与远端毫无关系**,必须 push 后远端才更新。
- 典型报错与冲突(conflict)(约 14:30–22:30,重点):
- 报错关键词:
hint: ... pull before push again,即"更新被远端拒绝,因为本地分支落后于服务器分支"。 - 原因:你本地 commit 了但还没 push 时,服务器上出现了新提交(别人 push 的、自己在 GitLab 网页上直接编辑的、或误操作)。Git 不能简单按时间戳拼接(不同时区、协作者作息不同),且两边可能改了同一个文件同一行,直接覆盖会丢失他人修改——这就是**冲突**。
- 解决流程:push 前先 pull 到本地,在本地梳理两个提交的先后、检查并解决冲突,这个过程叫**合并(merge)**;解决好冲突后再 commit、再 push。
- 冲突解决是**人工过程**:除非两人改的是不同行(如一个改 85 行、一个改 96 行)可以自动合并,否则改同一行就必须人工决定听谁的。
- 分支操作(约 23:15–26:45,结合 Learn Git Branching 演示):
git commit:提交,分支指针前移。- 新分支创建后与原分支指向同一个 commit(类似 C++ 里的指针);在当前分支上 commit 只会移动当前分支。
git checkout <分支名>:切换分支(将来有新命令switch,本课先记住 checkout)。git checkout -b <分支名>:创建**并**切换到新分支(课上示例git checkout -b bugFix)。- 典型团队工作流(约 26:45–28:35):不会直接在 main(如最新提交 c3)上改代码,而是从 main 分出
dev分支开发新功能、分出bugFix分支修复老错误;发新版本时把各分支 merge 回 main,解决所有冲突并保证不引入新错误后,main 指向最新提交再发布。 - 小乌龟实操(约 28:35–35:55):
- Switch/Checkout 时可从
remotes/origin下的远程分支(如 0x01)在本地创建同名分支并切换过去。 - 本地基于 main 新建 0x02 分支、新建文件夹
0x02、在其中写b64homework.py(故意不直接叫base64.py以免将来合并时冲突),commit 后 Revision Graph 出现分叉;此时远端还没有 0x02,push 后远端才出现(push 时 remote 分支名不填则默认与本地同名)。 - 建文件夹的习惯(约 33:30,老师强调的新知识):每次作业在自己的分支里建一个独立文件夹(0x01、0x02……0x0n)放代码,这样将来把各分支全部 merge 回 main 归档时,文件相对独立、不会因同名文件(都叫"作业.py")起冲突。非强制,但推荐养成。
- Pull Request(PR)(约 36:00–39:00):GitHub/GitLab 等平台的审核机制。开源项目通常把主干分支**保护(protect)起来,除 owner 外其他人不能直接 push 到 main;热心开发者想贡献代码时只能发起 **Pull Request——请主干维护者主动把你的内容"拉"过去合并,所以叫 pull request。之前课程仓库不让大家创建分支用的也是同一套 protect 机制。
二、Python 输入输出回顾¶
input()(约 39:30):内置函数,相当于 C 语言的scanf;input("请输入你的学号:")括号里是提示字符串,运行时打印提示并等待用户输入。- 字符串格式化(约 40:00–44:40):
- 按推荐优先级讲,最推荐 f-string:字符串前加小写
f,字符串内直接用{变量}大括号占位,打印时替换为变量值,如f"my teacher is {age} years old"。优点是直观,不懂 Python 的人也看得懂。 - 也讲了
format,但**.format()形式的任何操作都可以用 f-string 等价实现**,注意二者基本等价。 - 更精细的控制(左对齐/右对齐/居中、补零、浮点数格式等)需查 Python 官方文档的**格式化迷你语言(Format Specification Mini-Language)**,类似 C 语言
printf那套格式控制。 - 文件打开模式(约 45:10):类比 C 语言与英文首字母记忆——
r= read(读)、w= write(写)、x= exclusive create(排他创建,文件已存在则报错)、a= append(追加)、b= binary(二进制)、t= text(文本,默认模式,可省略不写)、+= update(读+写)。 - 老师观点:
input实际用得很少,文件读写得用很多——C 语言入门只教scanf极大限制了程序的输入能力,学文件操作才能处理图片等真实数据。
三、ASCII 字符画原理(练习)¶
- 目标(约 49:20):练习"读取特定文件格式 + 按特定方式输出",把 BMP 图像转成由字符组成的文本图。
- 原理(约 53:45–59:30):
- 彩色图每个像素由 RGB 三个字节组成,各 0–255 共 256 级(如红色 R=255, G=0, B=0)。
- 转成**灰度图**后每个像素只剩一个 0–255 的值:越大越亮(纯白≈255,纯黑≈0)。
- 不同字符打印出来"占的面积"(墨色浓度)不同:空格最白(当 255),
$更黑,扩展 ASCII 里还有实心黑方块可当 0。 - 取约 70 个可打印字符排成列表代表 70 级灰度:下标越小字符越黑,越靠近尾部越白。
- 逐像素取出 0–255 的灰度值,**量化映射**到 0–69 区间,从字符列表取对应字符写入文本文件,遍历完整张图即得字符画(思路和之前讲过的 base64 编码映射类似)。
- 量化方法(代码第 7 行附近):先算常量
256 / 70(即 256 ÷ 字符列表长度),每个像素的灰度值再除以该常量即得列表下标。 - 比例失真问题(约 58:40):512×512 的方图转出来变成长条,因为编辑器里的字符本身不是正方形(长方形),方形像素被拉高;选高宽相等的字体可避免拉伸。
- BMP 文件格式(约 1:01:30):文件头共 14 字节——前 2 字节是
BM标识,4 字节文件大小,2+2 字节保留位,4 字节位图数据偏移;其后约 **40 字节**的信息头里存着图像宽度、高度等。代码先读(跳过/解析)14 字节文件头,再读 40 字节信息头取出高宽,最后处理像素颜色。 - RGB→灰度公式(约 1:02:50,代码第 52 行):数字图像处理领域的经典公式,
灰度 = R×系数 + G×系数 + B×系数(加权求和),老师让学生自己查阅。 struct库与struct.unpack(约 1:03:40–1:09,难点):- 用途:分析二进制结构,相当于 C 语言结构体与字节串之间的转换(C 语言里可定义结构体,Python 原生表示不了这种定长布局,所以用
struct)。可打包(pack)也可解包(unpack),本课用解包。 - 用法:
struct.unpack(格式规则, 字节数据),按规则把字节一一解析进多个变量。 - 格式字符:开头
<表示字节序(大端 big-endian / 小端 little-endian,默认小端);2s= 2 字节字符串;I= 4 字节无符号整型(unsigned int);h= short;H= unsigned short。14 字节文件头可解出 5 个变量。 - 各字母对应关系要到官方文档查。老师评价:这个库对处理二进制数据相当有用,建议自学尝试,下节课再细讲。
示例与演示¶
- Learn Git Branching 过关演示(约 23:20–26:45):第一关完成两次
git commit过关;创建newImage分支观察两分支指向同一 commit;git checkout newImage后再 commit 使新分支前移;git checkout -b bugFix一步创建并切换。 - 小乌龟完整流程演示(约 28:35–35:55):克隆仓库到桌面 → Revision Graph 看图 → 从
remotes/origin/0x01switch 出本地 0x01 分支 → 切回 main 新建 0x02 分支 → 在 0x02 分支建0x02文件夹并用 VS Code 写b64homework.py→ add + commit → Revision Graph 出现分叉 → push(远端出现 0x02)。 - ASCII 字符画演示(约 45:35–1:03):
- 准备素材:从网上另存一张卡通图(webp),用画图软件把画布改成 666×666、图像缩放到 512×512 并锁定比例,另存为 24 位
test.bmp(演示程序写死了固定宽高,不够完备,学生可自行改进)。 - 新建
demo.py:先open一个空白文本文件用于写入(模式w,文本模式t省略),再以**二进制读**模式(rb)打开 BMP,处理后写入txt.txt。 - 运行结果:512×512、24 位的图被转成一片字符,缩小看是原图轮廓,放大看鼻子、眼睛都由具体字符组成;文本可以 Ctrl+A 全选删除,证明确实是纯文本。
- 拓展思路:用 ffmpeg 把视频逐帧抽成 BMP → 每帧映射成字符文本 → 文本截图 → 再用 ffmpeg 合成,即可做 ASCII 字符版视频。
重点与难点¶
- push 前先 pull:本地 commit 不会同步到远端;远端出现本地没有的提交时 push 会被拒绝(
pull before push again),必须 pull 下来在本地 merge、人工解决冲突后再 push。这是本课花最多篇幅讲的核心逻辑。 - 冲突只能人工解决:改同一文件同一行必然冲突,自动合并只适用于改动互不重叠的情况;Git 是协作平台,不能假设只有自己一个人在提交(包括网页端误操作也算远端变更)。
- 分支是指针:分支只是指向 commit 的名字,checkout 切换的是 HEAD 位置;
git checkout -b是创建+切换的合写。 - PR 机制:受保护的主干分支不能直接 push,外部贡献通过 Pull Request 由维护者合并。
- 作业归档习惯:每次作业放独立文件夹,避免多分支合并时同名文件冲突(老师特别强调的"新知识")。
- 格式化:f-string 优先;
.format()与 f-string 基本等价;对齐/补零/浮点精度要查官方格式化迷你语言。 - 文件模式:
t是默认模式可省略;读 BMP 必须用二进制模式b。 struct.unpack是本课自认最难的点:格式字符(<、2s、I、h、H)与字节序、BMP 14 字节文件头 + 40 字节信息头的布局需要对着官方文档理解。
关联内容¶
- 前置知识:之前课程已讲过 Git 基本操作(clone/commit/push,用 TortoiseGit 与 GitLab),本课是复习与深化;输入输出小节(
input、print、字符串格式化、文件读写)在课前视频中已详细讲过,本课仅快速回顾。 - 与后续课程的衔接:文件目录章节有一份作业,本课未布置(等新仓库建好后再在群里通知),下节课将结合输入输出与文件目录一起讲解并解决该作业;
struct库下节课再细讲。 - 与已学内容的呼应:灰度值→字符列表下标的映射逻辑类比此前讲过的 base64 编码(故演示文件起名
b64homework.py,并故意避开base64.py这个名字防止合并冲突);字节序(大端/小端)、定长二进制结构呼应 C 语言的数据结构与结构体知识;input类比scanf,格式化迷你语言类比printf。