代码处理配方:批量重命名、提取 import、清理注释、JSON 字段提取
四个"每天都会遇到"的代码层场景
本章把正则放回它的老家——处理代码与文件。四个场景覆盖大多数日常:重命名文件、梳理依赖、清理注释、从日志式 JSON 行里抠字段。每个配方都遵循同一纪律:先声明边界,再动手。
配方一:批量重命名
把下载目录里一堆 2024-10-26_报告_v2.docx 重命名成 报告_2024-10-26_v2.docx。用 Python 的 re.sub 生成新名字:
import re
def rename(name):
return re.sub(r"^(\d{4}-\d{2}-\d{2})_(.+?)_v(\d+)(\.\w+)$",
r"\2_\1_v\3\4", name)
for s in ["2024-10-26_报告_v2.docx", "2024-11-02_发票_v1.pdf"]:
print(rename(s))
# 输出:报告_2024-10-26_v2.docx
# 输出:发票_2024-11-02_v1.pdf
(.+?) 用懒惰量词吃标题段,(\.\w+)$ 锚定扩展名——中间内容再乱也不影响两端结构。批量执行时记得先打印"旧名 → 新名"清单人工过目,再真正调用 os.rename。
配方二:提取与改写 import
梳理一个 Python 项目的依赖,第一步是把所有 import 抓出来归类:
import re
src = """
import os
import re as pattern
from collections import Counter, defaultdict
from myapp.utils import helper
"""
print(re.findall(r"^from (\S+) import", src, re.M))
print(re.findall(r"^import (\S+?)(?: as (\S+))?$", src, re.M))
# 输出:['myapp.utils']
# 输出:[('os', ''), ('re', 'pattern')]
^ 配 re.M 逐行匹配;(?: as (\S+))? 把可选别名做成第二分组,findall 返回元组正好方便落库。同样的思路在 JS 里可以提取 require 与 import ... from,只是模块路径的引号要兼容单双两种。
配方三:清理注释——小心字符串里的陷阱
删掉代码里的 // 注释 看似一行正则的事:
import re
src = 'const url = "https://api.example.com"; // 拉取订单'
print(re.sub(r"//.*$", "", src))
# 输出:const url = "https:
结果惨不忍睹:字符串里的 https:// 被当成了注释起点。这是一个用正则必须认输的典型场景——正确区分"字符串里的斜杠"和"注释斜杠"需要状态机,正则的表达力不够。两条出路:用语言的词法解析器(如 Python 的 tokenize、JS 的 AST 工具),或退而求其次只在"确认没有字符串干扰"的文件类型(如 shell 配置)上用正则。知道正则在哪里该收手,比会用正则更专业。
相对安全的是块注释清理(/* ... */ 在多数语言里不能出现在合法字符串内部的情况极少):
import re
src = "let a = 1; /* 旧逻辑:v1 */ let b = 2;"
print(re.sub(r"/\*.*?\*/", "", src, flags=re.S))
# 输出:let a = 1; let b = 2;
配方四:JSON 字段提取的两条路线
先说结论:完整 JSON 用 json.loads 解析,永远别用正则——嵌套、转义、字符串内含引号,任何一条都能击穿正则。但有一类"JSON Lines"场景(每行一个扁平 JSON,如埋点日志),提取单个字段用正则反而轻快:
import re
row = '{"event":"order_paid","oid":"DD202410260018","amount":199.00,"ts":1729925527}'
m = re.search(r'"oid":"([^"]+)"', row)
print(m.group(1))
# 输出:DD202410260018
"键":"([^"]+)" 这个模板要求字段值是简单字符串且键值对里没有转义引号——前提写在注释里,遵守才有资格偷懒。一旦 JSON 出现嵌套对象或数组,立即切回解析器。
场景判断表
| 场景 | 正则适合度 | 理由与替代 |
|---|---|---|
| 批量重命名 | 适合 | 名字结构扁平,正则天然擅长 |
| 提取 import | 适合 | 行结构固定,方言简单 |
清理 // 注释 |
不适合 | 字符串与注释歧义,需词法器 |
| 清理块注释 | 勉强适合 | 加 re.S 的懒惰匹配基本够用 |
| 解析完整 JSON | 不适合 | 用 json / 解析器 |
| JSONL 扁平行抠字段 | 适合 | 无嵌套、无转义时成立 |
避坑提醒:这四个配方共享一条元规则——正则适用于"扁平、无歧义、结构稳定"的文本;一旦出现嵌套或同形异义(同样的字符在不同位置含义不同),立刻换工具。判断只需问一句:这段文本里,同样的字符序列会不会出现在两种语义位置?会,就不该用正则硬啃。
补一个完整脚本:重命名预演器
配方一只有核心替换,真实批量重命名还需要"预演 + 确认"的外壳——直接改文件名是危险操作,先生成映射清单:
import re
files = ["2024-10-26_报告.docx", "2024-11-02_发票.pdf", "随手记.txt"]
for old in files:
new = re.sub(r"^(\d{4}-\d{2}-\d{2})_(.+)(\.\w+)$", r"\2_\1\3", old)
mark = "rename" if new != old else "skip"
print(f"[{mark}] {old} -> {new}")
# 输出:[rename] 2024-10-26_报告.docx -> 报告_2024-10-26.docx
# 输出:[rename] 2024-11-02_发票.pdf -> 发票_2024-11-02.pdf
# 输出:[skip] 随手记.txt -> 随手记.txt
正则没命中的文件自然落进 skip 分支——用"替换前后是否相同"来区分命中与未命中,比单独写一条判断正则更省事也更不易错。人眼过一遍清单、确认无误后再把 print 换成 os.rename,批量操作的风险就锁在了预演阶段。这个"预演-确认-执行"三段式适用于一切文件级正则操作。
与版本控制配合的替换纪律
代码层的正则批量替换,纪律比技巧重要。三条实践:
- 用
git grep限定目标:git grep -l "旧接口名" -- '*.py'先列出命中文件,替换范围一目了然,未纳入版本控制的临时文件天然被排除。 - 一次提交只做一类替换:格式类替换(引号、缩进)与语义类替换(接口名、变量名)分开提交,diff 干净,回滚精准。
- 替换后先跑测试再提交:正则替换是"文本手术",测试是唯一能证明"手术没伤到器官"的证据。
git grep -n "DD2024" -- '*.md' | head
git diff --stat
第一条确认改动点清单,第二条用 diff 统计复核改动面——两条命令加起来十秒钟,换来的是"批量替换后心里有底"。正则事故几乎都发生在"改完了但不知道改了什么"的时刻,版本控制就是那道后悔药,别嫌取药麻烦。
参考资源
- Python tokenize 模块(注释清理的正规军):tokenize
- Python re.sub 文档:re — Regular expression operations
本章由 WorkBuddy 原创撰写,示例均经实际验证。
