利用 Python 脚本批量创建空白 Markdown 笔记
文章目录
- 利用 Python 脚本批量创建空白 Markdown 笔记
- 1 背景介绍
- 2 需求描述
- 3 明确思路
- 4 具体实现
- 4.1. 遍历 toc.md 文件,收集文件名和对应的文件内容
- 4.2. 实现文件批量生成逻辑
- 4.3. 补全缺失的工具函数
- 4.4. 进一步补全工具函数中的工具函数
- 5 脚本运行
- 6 注意事项
利用 Python 脚本批量创建空白 Markdown 笔记
1 背景介绍
我学视频课有个习惯:每学完一节课,就相应创建一个 Markdown 格式的纯文本自学笔记,方便日后查阅。久而久之,要创建的笔记文件越来越多,如果再要求这些文件都具备统一的格式(例如文件名的命名风格、统一的标题模板等等),无形中就会多出不少工作量。之前我都是用 PowerShell + ChatGPT 来批量生成这些文件夹和文件名,虽然脚本编写比较繁琐,但可以一劳永逸,倒也划算;前段时间入坑 Python,正好可以练练文件操作和正则表达式,才发现 Python 处理起这类问题来比 PowerShell 轻松太多了。
2 需求描述
这里随便找了一个视频课,原始的课程目录长这样:

【图 1:原始课程目录结构(节选)】
复制到一个临时文件 toc.md 后长这样:

【图 2:复制到临时文件 toc.md 后的课程目录(节选)】
而我想要的效果,是用 Python 脚本批量生成这样的文件结构:

【图 3:最终希望实现的笔记目录结构与内容模板】
3 明确思路
再复杂的需求也可以拆分成若干个可以轻松实现的单元。多行的批量处理可以先简化为某一行的处理:
- 读取
toc.md的某一行,赋给一个变量line; - 判定
line是按文件夹处理,还是按.md文件处理(通过正则表达式判定);- 是文件夹:
- 创建该文件夹;
- 提取章节编号(如
Ch01),以便本章笔记文件使用; - 重置笔记文件的二级目录编号(比如上一行编号为
Ch01.2,这一行需要重置为Ch2.1)
- 是文件:
- 获取当前章节编号,生成对应的文件名
- 获取当前章节编号,生成对应的
Markdown文本内容 - 二级目录编号递增
1
- 是文件夹:
- 遍历完
toc.md后,根据文件名是否以.md结尾,可以判定生成的是文件夹还是Markdown文件:- 是文件夹:创建该文件夹,并更新当前路径(
curr_path,以便后续笔记文件引用); - 是文件:根据当前路径生成对应的笔记文件,同时写入文本内容。
- 是文件夹:创建该文件夹,并更新当前路径(
4 具体实现
4.1. 遍历 toc.md 文件,收集文件名和对应的文件内容
创建 Python 脚本文件 generate_files.py,确定信息采集逻辑:
def make_chapter(line):pass
def make_section(line, chp_num, sec_num):pass# 参数初始化
chp_num = '0' # 章节号
sec_num = 1 # 小节号
(contents, file_names) = ([], []) # 初始化章节内容、文件名# 读取课程目录文件
with open('./toc.md', 'r') as f:# 读取文件的每一行,去掉行尾的换行符,然后存入列表 lineslines = [l.strip() for l in f.readlines()] for line in lines:# 判断行首是否是数字,如果是,说明是章节标题starts_with_digit = re.match(r'^(\d+)\.', line)if starts_with_digit:# 如果是章节标题,则生成该章节的 文件名 和 章节号(file_name, chapter_num) = make_chapter(line)# 添加本章的章节内容(仅占位用,文件夹没有文本内容)contents.append('placeholder')# 添加本章的文件名file_names.append(file_name)# 更新章节号chp_num = chapter_numelse:# 如果不是章节标题,则生成该小节的 文本内容 和 文件名(sec_file_name, sec_content) = make_section(line, chp_num, sec_num)# 添加该小节的文本内容file_names.append(sec_file_name)# 添加该小节的文件名contents.append(sec_content)# 按本行的实际作用动态更新小节编号sec_num = 1 if starts_with_digit else (sec_num + 1)
注意:make_chapter 函数(L1、L22)和 make_section 函数(L3、L34)暂不实现,先确定大流程。
4.2. 实现文件批量生成逻辑
利用 4.1 的信息采集逻辑,就可以批量生成文件/文件夹了:
# 批量生成文件
curr_path = ''
for index, (file_or_path, cont) in enumerate(zip(file_names, contents)):if file_or_path.endswith('.md'):file = file_or_pathwith open(f'./{curr_path}/{file}', 'w') as f:f.write(cont + '\n')else:path = file_or_pathos.makedirs(path, exist_ok=True)curr_path = path
4.3. 补全缺失的工具函数
大流程确定后,再来实现 make_chapter 函数和 make_section 函数:
#!/usr/bin/env pythonimport re
import osdef dashed(s, sep=' '):passdef sanitize_filename(s):passdef repl(match):passdef make_chapter(line):"""Generates the specific file name and the corresponding chapter number for this chapter.Args:line (string): The original line of text to be processed.Returns:tuple: A tuple containing the file name and the chapter number.Example:Input: line: '1. Getting Started with GitHub Actions'Output: ('Ch01_Getting_Started_with_GitHub_Actions.md', '1')"""pttn = r'(\d+)\.(.*)'re.findall(pttn, line)file_name = re.sub(pttn, repl, line)chapter_num = re.sub(pttn, r'\1', line) return (file_name, chapter_num)def make_section(line, chapter_num, section_num):"""Generates the specific file name and corresponding content for this section.Args:line (string): The original line of text to be processed.chapter_num (string): The current chapter number.section_num (int): The current section number.Returns:tuple: A tuple containing the file name and the content of the section.Example:Input: line: ' - Welcome to the Course!'chapter_num: '1'section_num: 1Output: ('Ch01_1_Introduction_to_the_Course.md', '## Ch01.1 Introduction to the Course')"""line = line.strip('- ')header_part = f'Ch{chapter_num.zfill(2)}_{section_num}'content_part = dashed(line, '_').replace('---', '_')file_name = f'{header_part}_{content_part}'title_content = f'## Ch{chapter_num.zfill(2)}.{section_num} {line}'return (f'{sanitize_filename(file_name)}.md', title_content)
4.4. 进一步补全工具函数中的工具函数
为了方便管理,再对工具函数中出现的几个工具函数做进一步实现:
def dashed(s, sep=' '):"""Removes leading and trailing whitespaces and replaces all other whitespaces with the specified separator.Args:s (str): The original string to be processedsep (str, optional): The separator string. Defaults to ' '.Returns:string: The processed string."""return sep.join(s.strip().split())def sanitize_filename(s):"""Sanitizes the file name by removing all characters except letters, numbers, underscores, and dots.Args:s (string): The original file name to be sanitized.Returns:string: The sanitized file name."""# 只保留字母、数字、下划线和点,其他字符都删除pttn = r'[^\w -]'re.findall(pttn, s)return re.sub(pttn, '', s)def repl(match):"""Generates the specific file name for this chapter.Args:match (Match[string]): The matched object from the regular expression.Returns:string: The file name for this chapter."""num = match.group(1)content = match.group(2)file_name = f'Ch{num.zfill(2)}_{dashed(content, "_")}'return sanitize_filename(file_name)
5 脚本运行
使用以下命令运行脚本:
python generate_files.py
不到两秒,就生成了所有的文件夹和 Markdown 空白文件:

【图 4:检查最终的批量生成结果(符合预期)】
6 注意事项
由于是第一次尝试 Python 脚本,调试过程中走了不少弯路,这里集中梳理一下:
- 一定要小步走,多迭代;
- 按抽象层次依次实现每一层的函数逻辑,方便后续管理;
- 读取每一行文本都会包含一个
\n换行符,需要用str.strip()处理掉; - 先用
re.findall捕获匹配结果,然后再用re.sub执行替换,否则后者始终输出空字符串; - 访问捕获的结果时,注意
f-string和r-string中反斜杠\的不同写法,前者要写成\\,而后者直接写成\即可; - 脚本运行前提前做好备份(以避免
rm -Recurse -Force *把Python脚本文件本身也删没了的杯具。。); - 尽量避免引用全局变量,多用参数传参(本例必须使用全局变量,以实时获取当前的文件夹名称和路径);
- 主体框架确定后,今后只需要修改文件夹和文件的处理逻辑,就可以适应不同的原始数据。
os.makedirs(path, exist_ok=True)中的exist_ok参数用于控制在指定路径已经存在时的行为:- 当
exist_ok=True时,如果目标目录已经存在,makedirs不会抛出异常(本例暂不涉及); - 当
exist_ok=False(默认值)时,如果目标目录已存在,则会抛出FileExistsError异常。 - 重视代码注释和函数文档(docstring)的书写,既方便别人,也方便自己(主要是写给自己看的)
相关文章:
利用 Python 脚本批量创建空白 Markdown 笔记
文章目录 利用 Python 脚本批量创建空白 Markdown 笔记1 背景介绍2 需求描述3 明确思路4 具体实现4.1. 遍历 toc.md 文件,收集文件名和对应的文件内容4.2. 实现文件批量生成逻辑4.3. 补全缺失的工具函数4.4. 进一步补全工具函数中的工具函数 5 脚本运行6 注意事项 利…...
【Qt】C++11 Lambda表达式
1. 举例 connect(ui->pushButton, &QPushButton::clicked, [](bool checked){//具体代码qDebug() << "Hello" << checked;}); 2. 详情 //完整形式 [ capture ] ( params ) opt -> ret { body; }; capture 是捕获列表params 是参数表opt 是函数…...
怎样提高服务器中的数据传输速度?
服务器中的数据传输速度会影响着用户的体验感,当企业中的数据传输速度出现卡顿或者是过慢时,用户不能及时浏览到所需的内容,给用户造成不好的体验感,那么企业该怎样才能提高服务器中的数据传输速度呢? 服务器之间如何传…...
Vue 封装公告滚动
文章目录 需求分析1. 创建公告组件Notice.vue2. 注册全局组件3. 使用 需求 系统中需要有一个公告展示,且这个公告位于页面上方,每个页面都要看到 分析 1. 创建公告组件Notice.vue 第一种 在你的项目的合适组件目录下(比如components目录&a…...
JVM实战—12.OOM的定位和解决
大纲 1.如何对系统的OOM异常进行监控和报警 2.如何在JVM内存溢出时自动dump内存快照 3.Metaspace区域内存溢出时应如何解决(OutOfMemoryError: Metaspace) 4.JVM栈内存溢出时应如何解决(StackOverflowError) 5.JVM堆内存溢出时应该如何解决(OutOfMemoryError: Java heap s…...
【python翻译软件V1.0】
如果不想使用密钥的形式,且需要一个直接可用的中英文翻译功能,可以使用一些免费的公共 API,如 opencc 或其他无需密钥的库,或直接用 requests 获取翻译结果。 其中,我可以给你一个简单的代码示例,使用 tra…...
Spring Boot中的依赖注入是如何工作
Spring Boot 中的依赖注入(Dependency Injection,简称 DI)是通过 Spring 框架的核心机制——控制反转(Inversion of Control,IOC)容器来实现的。Spring Boot 基于 Spring Framework,在应用中自动…...
ubuntu22.04 编译安装libvirt 10.x
环境安装 sudo apt-get update -y sudo apt-get install qemu-system-x86 bridge-utils libyajl-dev -y sudo apt-get install build-essential autoconf automake libtool -y sudo apt-get install libxml2-dev libxslt1-dev libgnutls28-dev libpciaccess-dev libnl-3-de…...
[fastadmin] 第三十四篇 FastAdmin 商城模块标签使用详解
FastAdmin 商城模块标签使用详解 一、标签基本语法 1.1 基础语法格式 {shop:goodslist flag"参数值" id"变量名" row"数量"}<!-- 循环内容 --> {/shop:goodslist}1.2 常用参数说明 flag: 商品标记筛选id: 循环变量名row: 显示数量 1.…...
(2024,LLaVA-Bench (Wilder),LLaVA-NeXT,LLaMA3,Qwen-1.5,语言模型扩展)
LLaVA-NeXT: Stronger LLMs Supercharge Multimodal Capabilities in the Wild 目录 1. 简介 2. 探索大规模语言模型的能力极限 3. LLaVA-Bench (Wilder):日常生活视觉聊天基准 4. Benchmark 结果 1. 简介 我们通过引入近期更强大的开源大语言模型(…...
IPEX-LLM开发项目过程中的技术总结和心得
IPEX-LLM开发项目过程中的技术总结和心得 在人工智能快速发展的时代,高效地开发和部署大语言模型(LLM)已成为技术人员的必备技能。在我们的项目中,我们采用了 Intel Extension for PyTorch(简称 IPEX)和 L…...
HTTP/HTTPS ②-Cookie || Session || HTTP报头
这里是Themberfue 上篇文章介绍了HTTP报头的首行信息 本篇我们将更进一步讲解HTTP报头键值对的含义~~~ ❤️❤️❤️❤️ 报头Header ✨再上一篇的学习中,我们了解了HTTP的报头主要是通过键值对的结构存储和表达信息的;我们已经了解了首行的HTTP方法和UR…...
【软考】软件设计师
「学习路线」(推荐该顺序学习,按照先易后难排序) 1、上午题—计算机系统(5~6分)[1.8; ] 2、上午题—程序设计语言(固定6分)[1.9; ] 3、下午题—试题一(15分) 4、上午题—…...
K8s Pod OOMKilled,监控却显示内存资源并未打满
1. 问题现象 pod一直重启,通过grafana查看,发现内存使用率并没有100%。 2. 排查过程 2.1 describe查看pod最新一次的状态 可以明显看到,最近一次的重启就是因为内存不足导致的。 2.2 describe 查看node节点状态 找到原因了,原来…...
C++ 原子变量
C 原子变量 文章目录 C 原子变量1. 原子变量是什么?2. 原子操作的特点3. 原子变量的作用1. 多线程安全的共享数据访问2. 替代锁机制3. 实现低级同步算法 4. 原子变量的常见操作5. 内存顺序(Memory Ordering)内存顺序控制在原子变量中的作用如…...
linux网络 | http结尾、理解长连接短链接与cookie
前言:本节是http章节的最后一部分,主要解释一些小概念。讲解到了HTTP的方法,表单, 重定向等等。 现在废话不多说, 开始我们的学习吧。 ps:本节内容都是概念, 知道就行, 友友们放心观…...
金融项目实战 02|接口测试分析、设计以及实现
目录 ⼀、接口相关理论 二、接口测试 1、待测接口:投资业务 2、接口测试流程 3、设计用例理论 1️⃣设计方法 2️⃣工具 4、测试点提取 5、测试用例 ⼀、接口相关理论 1、ui功能测试和接⼝测试那个先执⾏?为什么? 结论:…...
二、智能体强化学习——深度强化学习核心算法
2.1 DQN 系列及其改进 2.1.1 背景与动机 在经典强化学习中(如 Q-Learning),如果状态空间或动作空间非常大乃至连续,那么用一个表格来存储 Q ( s , a ) Q(s,a) Q(s,a) 不再可行。为了解决该问题,可以使用神经网络来逼…...
Mysql--架构篇--存储引擎InnoDB(内存结构,磁盘结构,存储结构,日志管理,锁机制,事务并发控制等)
MySQL是一个多存储引擎的数据库管理系统,支持多种不同的存储引擎。每种存储引擎都有其独特的特性、优势和适用场景。选择合适的存储引擎对于优化数据库性能、确保数据完整性和满足业务需求至关重要。 注:在同一个Mysql的数据库中,对于不同的表…...
JVM实战—13.OOM的生产案例
大纲 1.每秒仅上百请求的系统为何会OOM(RPC超时时间设置过长导致QPS翻几倍) 2.Jetty服务器的NIO机制如何导致堆外内存溢出(S区太小 禁NIO的显式GC) 3.一次微服务架构下的RPC调用引发的OOM故障排查实践(MAT案例) 4.一次没有WHERE条件的SQL语句引发的OOM问题排查实践(使用MA…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
