跳转至

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/0x01 switch 出本地 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。