ASCII码速查表这东西,我见过太多人把它打印出来贴在显示器边框上,用的时候照样得低头去找。更常见的场面是:写一个解析二进制协议的脚本,卡在"换行到底是10还是13"上,翻文档翻了五分钟;或者处理一段文本,想判断某个字符是不是控制字符,写了个if ch in '\n\r\t'就以为覆盖全了。这事的荒诞之处在于,你完全可以用 Python 花三分钟生成一张真正属于自己的 ASCII 码速查表,分组方式、进制组合、字号大小全按你的使用习惯来,而不是迁就网上下载的那张看不清的图片。
这篇内容讲的就是这件事的完整做法:从 ASCII 编码的排布规律讲起,用ord()和chr()两个内置函数把整张表"算"出来,然后分别输出成控制台表格、可直接打印的 HTML、CSV 和 16×8 网格图。中途会把终端对齐、HTML 转义、Excel 编码、字体路径这几个真实踩过的坑一个个拆开。适合刚学 Python 不久、想找个能跑通又有实际用处的小项目练手的读者,也适合手头正好需要一个可定制查表工具的老手直接抄代码。
1. 先把"背"这件事拆掉:ASCII 的排布本身就是一张结构图
很多人觉得 ASCII 难记,是因为脑子里把它当成 128 个孤立数字的集合。实际上它是一套有严格分区规则的 7 位编码,第 0 位到第 6 位,一共 128 个码位,每一段干什么用是定死的。搞清楚分区,你就不需要记 128 个数字,只需要记 4 个边界值。这一步想通了,后面写代码只是把脑子里的结构翻译成循环。
1.1 128 个码位被切成四块,每块职责清楚
把 0 到 127 按十六进制的高位切开,四段的分工非常干净:0x00–0x1F是 32 个控制字符,负责"让设备做事"而不是"显示一个字形";0x20–0x3F是空格加标点加数字,这一段里数字刚好占了0x30–0x39;0x40–0x5F是大写字母加几个符号,大写字母占0x41–0x5A;0x60–0x7F是小写字母加符号,小写字母占0x61–0x7A,最后0x7F单独是 DEL。
你会发现每一段的开头都留了一个或几个符号位。0x40是@,0x60是反引号,0x5B到0x60之间塞的是[ \ ] ^ _ \``。这些"边角符号"之所以出现在这个位置,是因为当年打孔卡和电传打字机的键盘布局就是这么排的,字母区两侧自然留下了符号区。知道这个来历,你就不会觉得0x5B是[` 这件事很随机了。
1.2 大小写差 32,是因为编码里专门留了一位做切换
A是 65,a是 97,差 32。B是 66,b是 98,还是差 32。这不是巧合,而是设计者故意把第 5 位(从 0 开始数,权重 32 的那一位)留成了大小写开关位。大写字母这一位置 0,小写字母这一位置 1,其余 5 位完全相同。
这个设计的直接好处是:判断两个字母是不是同一个字母的大小写形式,只需要比较ord(c1) & 0xDF == ord(c2) & 0xDF;把大写转小写,只需要ord(c) | 0x20。虽然 Python 里直接用str.lower()更省事,但理解这个位关系,能让你在写底层解析、做字符归一化的时候少绕很多弯。同理,数字0到9是0x30到0x39,字符'7'转数字只要ord(ch) - 48,这个减法在任何语言里都通用,比int(ch)更贴近底层。
1.3 控制字符属于"该查名字,不该背缩写"的那一类
0 到 31 这 32 个控制字符,缩写是 NUL、SOH、STX 一直到 US,再加一个 DEL。老实说,除了\n(LF,10)、\r(CR,13)、\t(HT,9)、\0(NUL,0)、\b(BS,8)、\f(FF,12)、\v(VT,11)这七八个日常会用到的,剩下二十几个一辈子也碰不上几次,背它们纯粹是浪费脑容量。
正确的做法是把它们做成一张字典放进代码里,需要的时候查一眼。这也是我坚持自己生成速查表而不是背表的核心原因:记忆的容量应该留给规律,细节交给工具。你在第 2 章会看到,这张字典只有三十来行,一次写好,以后任何项目里复制粘贴都能用。
2. 用 ord 和 chr 把表"算"出来,而不是"抄"出来
Python 里跟 ASCII 打交道,翻来覆去就是两个函数:ord()把单个字符转成码位整数,chr()把整数转回字符。它们互为反函数,覆盖的不只是 ASCII,而是整个 Unicode 范围,但对 0 到 127 这段来说,行为和 ASCII 完全一致。整个速查表项目的地基就是这两个函数加一个range(128)。
2.1 两个函数的关系,用一句话说清
ord('A')返回 65,chr(65)返回'A'。ord的参数必须是长度为 1 的字符串,传'AB'会直接抛TypeError;chr的参数必须是 0 到 0x10FFFF 之间的整数,超出范围抛ValueError,传负数也抛ValueError。这两个异常在实际写代码时经常遇到,尤其是从文件里读进来一个空字符串再传给ord,报错信息是ord() expected a character, but string of length 0 found,第一次见容易懵。
有个细节值得记住:ord和chr处理的"字符"在 Python 3 里是 Unicode 码位,不是字节。所以ord('中')返回 20013,而不是某个字节值。这一点在第 6 章讲编码边界的时候还会再提到,因为很多人第一次跑ord看到中文返回一个大数就慌了,以为程序出错。
2.2 最小可运行版本,十二行就能出结果
先把可打印区跑通,验证思路没问题,再往上加功能。这是我一贯的做法:先拿到一个能看见输出的最小闭环,再谈格式美化。
# ascii_min.py for code in range(32, 127): ch = chr(code) print(f"{code:>3} 0x{code:02X} {ch}")三行核心逻辑,跑出来的结果就是 95 行,从空格(32)一直到波浪号(126)。{code:>3}是右对齐补空格到 3 位,{code:02X}是十六进制大写、不足两位补零。这两个格式化写法建议直接记住,后面所有导出功能都要用。
跑完之后你大概率会注意到:空格那一行打印出来是"空"的,末尾看不见。这就是第一个要处理的显示问题,第 3 章专门讲。另外 127 是 DEL,属于控制字符,所以range(32, 127)刚好把可打印区框住,不多不少。
2.3 控制字符的名字,别去依赖 curses 模块
搜"Python ASCII 控制字符名"的时候,很多答案会指向curses.ascii.controlnames。这个数组确实存在,长度 33,下标 0 到 32 对应 NUL 到 DEL。问题是curses在 Windows 上的标准发行版里根本没有,一import就报ModuleNotFoundError。如果你的脚本要在多种机器上跑,这条路直接堵死。
稳妥的做法是自己写一个字典,把常用的转义序列一起塞进去。下面这份是我一直在用的版本,三十来行,覆盖全部控制字符,同时把对应的 Python 转义写法标出来,查表的时候一眼能看到"这个字符在字符串里怎么写":
# ctrl_names.py CTRL = { 0: ("NUL", r"\0"), 1: ("SOH", ""), 2: ("STX", ""), 3: ("ETX", ""), 4: ("EOT", ""), 5: ("ENQ", ""), 6: ("ACK", ""), 7: ("BEL", r"\a"), 8: ("BS", r"\b"), 9: ("HT", r"\t"), 10: ("LF", r"\n"), 11: ("VT", r"\v"), 12: ("FF", r"\f"), 13: ("CR", r"\r"), 14: ("SO", ""), 15: ("SI", ""), 16: ("DLE", ""), 17: ("DC1", ""), 18: ("DC2", ""), 19: ("DC3", ""), 20: ("DC4", ""), 21: ("NAK", ""), 22: ("SYN", ""), 23: ("ETB", ""), 24: ("CAN", ""), 25: ("EM", ""), 26: ("SUB", ""), 27: ("ESC", r"\x1b"), 28: ("FS", ""), 29: ("GS", ""), 30: ("RS", ""), 31: ("US", ""), 127:("DEL", ""), }注意\e这个转义在 Python 字符串里是不存在的,虽然有些语言支持,但 Python 会把它当成普通字符e。ESC 只能写成\x1b,这个坑我在早期代码里踩过一次,字符串里想插 ESC 结果插进去的是字母 e,排查了半小时。
3. 控制台版本的对齐问题:中文表头一定错位
第一版代码跑通之后,下一步自然是加表头、加类别列、加二进制列。这时候就会撞上控制台排版最经典的问题:中文字符在终端里占两个字符宽度,但str.format的对齐会把它当成一个宽度计算。你写f"{'字符':^8}",它补的空格数是对的,但显示出来整体右移一格,整个表格就歪了。
3.1 等宽字体只保证 ASCII 等宽,不保证中文等宽
终端用的是等宽字体,但"等宽"指的是拉丁字母之间的宽度一致。中文、日文、韩文这些东亚字符在绝大多数等宽字体里被渲染成两个拉丁字符宽,这叫全角。Python 的len()返回的是码位个数,'中'的len是 1,但在屏幕上占 2 个格子。这两套计量单位不一致,对齐自然崩。
如果你的表头全用英文,比如DEC、HEX、OCT、BIN、CHAR、KIND,问题就不存在。但我个人更习惯中文表头,看着舒服,所以还是选择正面解决。方案是:自己算显示宽度,不用format的对齐功能。
3.2 手写一个宽度补白函数,二十行管所有场景
核心思路是用unicodedata.east_asian_width()判断每个字符的东亚宽度属性,返回W(宽)或F(全角)的算 2,其余算 1。这个函数是标准库,不需要装任何东西。
import unicodedata def wcswidth(s: str) -> int: """返回字符串在等宽终端中的显示宽度""" w = 0 for ch in s: w += 2 if unicodedata.east_asian_width(ch) in ("W", "F") else 1 return w def pad(s: str, width: int, align: str = "<") -> str: """按显示宽度补白,align 支持 < > ^""" gap = width - wcswidth(s) if gap <= 0: return s if align == "<": return s + " " * gap if align == ">": return " " * gap + s left = gap // 2 return " " * left + s + " " * (gap - left)这里有个容易忽略的点:east_asian_width对 emoji 和一些特殊符号的返回是W,但某些终端渲染 emoji 时宽度并不稳定。所以速查表里我一律不用 emoji,纯字符最保险。另外组合字符(比如带音标的字母)宽度计算会出偏差,但 ASCII 场景完全遇不到,不用管。
3.3 不可见字符的占位约定,决定了表好不好用
控制字符和空格在终端里是看不见的,如果不做占位,输出就是一串空白,完全没法用。我的约定是三条:控制字符显示成尖括号包起来的缩写,比如<LF>;空格显示成<SP>;其余可打印字符原样显示。再单开一列"字面量",用repr()的形式给出它在字符串里的写法。
def make_rows(): rows = [] for code in range(128): ch = chr(code) if code in CTRL: name, esc = CTRL[code] display, literal, kind = f"<{name}>", (esc or "-"), "控制" elif code == 32: display, literal, kind = "<SP>", "' '", "空白" else: display, literal, kind = ch, repr(ch), "可打印" rows.append((code, display, literal, kind)) return rowsrepr(ch)对普通字符返回带引号的字符串,比如'A',正好符合"字面量"这个语义。这一列在实际调试里价值很高:当你需要在代码里嵌一个制表符,直接照抄'\t'就行,不用去想它叫什么名字。
4. 生成一份能贴在显示器边的 HTML 速查表
控制台版本适合快速查看,但真正贴在墙上的东西,还是得能打印。导出格式我试过三种:纯文本、CSV、HTML。最后长期用的是 HTML,原因很实际。
4.1 选 HTML 不选 PDF 的理由
PDF 需要额外依赖,reportlab要学一套绘图 API,weasyprint要装一堆系统库,在有些环境下光装依赖就得折腾半小时。HTML 只需要一个浏览器,打印功能是现成的,Ctrl+P 就能出纸。更关键的是 HTML 的表头可以做position: sticky,在屏幕上滚动查看时表头不跑;颜色和字号随时改一行 CSS 就行,不用重新生成文件。
Markdown 表格也算一个选项,缺点是列宽完全交给渲染器决定,128 行铺开之后在有些编辑器里会变得特别宽,读起来累。CSV 则是另一个用途:你想在表格软件里自己筛选排序,CSV 是最合适的。所以我的做法是写一个通用的export()函数,用fmt参数切换,几分钟就能加一种新格式。
4.2 用占位符替换,别用 f-string 拼 HTML
这是本节最值得记的一条经验。HTML 的<style>块里全是{},如果你用 f-string 写模板,每一个 CSS 大括号都要双写成{{和}},一个两百行的样式表改下来眼睛都花了,漏一个就报SyntaxError。改用str.replace加一个占位符,代码清爽得多:
HTML_TPL = """<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>ASCII 速查表</title> <style> body { font-family: "DejaVu Sans Mono", Consolas, monospace; margin: 20px; } table { border-collapse: collapse; font-size: 13px; } th, td { border: 1px solid #bbb; padding: 3px 8px; text-align: center; } thead th { background: #eee; position: sticky; top: 0; } td.ctrl { background: #fdf3e7; color: #8a5a00; } td.space { background: #eef6ff; color: #1f5f9e; } @media print { body { margin: 0; } thead th { position: static; } table { font-size: 11px; } } </style> </head> <body> <h3>ASCII 码速查表(0–127)</h3> <table> <thead> <tr><th>DEC</th><th>HEX</th><th>OCT</th><th>BIN</th><th>字符</th><th>字面量</th><th>类别</th></tr> </thead> <tbody> __ROWS__ </tbody> </table> </body> </html> """生成行的时候有一个必须处理的细节:HTML 转义。第 38 号字符是&,第 60 号是<,第 62 号是>。这三个如果不转义,浏览器会把&后面的内容当成实体引用,<后面的当成标签开头,表格结构直接被破坏,而且报错信息很难定位。用标准库的html.escape()处理一下就好:
import html def render_rows(rows): parts = [] for code, display, literal, kind in rows: cls = {"控制": "ctrl", "空白": "space"}.get(kind, "") parts.append( f'<tr>' f'<td>{code}</td>' f'<td>0x{code:02X}</td>' f'<td>0o{code:03o}</td>' f'<td>{code:07b}</td>' f'<td class="{cls}">{html.escape(display)}</td>' f'<td>{html.escape(literal)}</td>' f'<td>{kind}</td>' f'</tr>' ) return "\n".join(parts) def export_html(rows, path="ascii_table.html"): body = render_rows(rows) with open(path, "w", encoding="utf-8") as f: f.write(HTML_TPL.replace("__ROWS__", body))html.escape默认会把&、<、>、"、'都转掉,正好覆盖我们需要的情况。注意display列里<LF>这种尖括号也要转义,不然LF会被当成一个未知标签直接吞掉,表格里那一格就空了。这个现象我第一次遇到时以为是数据丢了,其实是浏览器把非法标签忽略了。
4.3 打印排版:黑白友好和分栏
贴在墙上的表,配色要考虑黑白打印机。#fdf3e7这种浅橙底在彩色打印时区分度很好,但黑白打印出来跟白底几乎一样,控制字符和可打印字符就分不出来了。所以我在@media print里没有把背景色全部去掉,而是加了一条print-color-adjust: exact;,让浏览器保留背景色。Chrome 和 Edge 都支持这个属性,Safari 需要用-webkit-前缀。
128 行铺成一张 A4 纸会很长,正常是两到三页。如果想让一页装下,可以缩到 11px 字号,行高压到 1.2,padding改成1px 4px,实测 128 行刚好能塞进一页。不过我不太推荐这么挤,贴在旁边看的时候眼睛累。分成两页反而舒服,一页 64 行,中间断开的位置正好在0x40附近,自然对应大写字母区的开头。
4.4 根据"个人高频错误"加一列标记
这是把通用表变成"我的表"的关键一步。你可以回想一下自己被哪些码位坑过:我个人的清单是\n和\r总搞混,0x30老是记成 48 还是 49,0x60和 0x60 附近的反引号经常写成单引号。在生成逻辑里加一个MISTAKES = {10, 13, 48, 49, 96}集合,命中的行在 CSS 里加一个左边框加粗,打印出来一眼就能看见。
这个思路其实适用于任何速查表项目。所谓"专属",不是排版花哨,而是把你自己踩过的坑显式标出来。别人做的表是通用最优,你自己的表是你的错误历史。
5. 16×8 网格视图:真正用来查的是这个布局
列表视图适合精确查找,但如果你只是想快速定位某个字符的码位,传统那种 16 列 × 8 行的网格图效率高得多。它的结构完全对应 ASCII 的位布局:行是高 3 位,列是低 4 位。比如A是 65,即0b1000001,高 3 位是100(十进制 4),低 4 位是0001(十进制 1),所以它在第 4 行第 1 列。这个"行4列1"的定位感,比在一堆数字里找要快得多。
5.1 用位移拼出网格,代码比想象中短
行号从 0 到 7,每行乘以 16 就是这一行的起始码位,加上列号 0 到 15 就是当前格子的码位。用(hi << 4) | lo是同一个意思,位移比乘法更能体现"高 3 位 + 低 4 位"的结构,我习惯用位移。
def grid_text(): lines = [] lines.append(" " + "".join(f" {lo:X} " for lo in range(16))) lines.append(" " + "-" * 64) for hi in range(8): cells = [] for lo in range(16): code = (hi << 4) | lo if 33 <= code <= 126: ch = chr(code) elif code == 32: ch = "␣" else: ch = "·" cells.append(f" {ch} ") lines.append(f" {hi:X}0 |" + "".join(cells)) return "\n".join(lines)空格用␣(U+2423)这个可见空格符号代替,控制字符用·(U+00B7)占位。这两个符号在大多数等宽字体里都有,不会显示成豆腐块。如果你担心字体不支持,改用.和_也行,只是辨识度低一点。
5.2 网格图最实用的地方是看规律
把这张图打印出来放在桌上,你会直观看到几个以前需要背的东西:0x40那一行从@开始,A到Z连续排;0x60那一行从反引号开始,a到z连续排;数字0到9在0x30那一行的中间偏右。左右两半的对称关系一目了然:A在行 4 列 1,a在行 6 列 1,两者的列号一样,行号差 2,正好对应 32 的差值。
这种"看一眼就懂"的效果,是任何列表形式的表格都给不了的。我建议两个视图都生成,列表用来查具体值,网格用来建立直觉。
5.3 两种视图的取舍和组合方式
如果只做一张图贴墙,我会选网格;如果要在脚本里查某个码位对应什么字符,列表更好用,因为可以用grep或者Ctrl+F定位。所以最终的脚本里两个函数都保留,用命令行参数切换:
python ascii_table.py --fmt grid # 终端里打印网格 python ascii_table.py --fmt html # 生成 HTML 文件 python ascii_table.py --fmt csv # 生成 CSV python ascii_table.py --fmt markdown # 生成 Markdown 表格用argparse加四行就能实现,choices参数还能自动做输入校验,输错了会提示可用选项。这种小工具一旦有了命令行入口,使用频率会明显提高,因为它可以塞进任何脚本里当子命令调用。
6. 四个真实踩过的坑:从编码边界到字体路径
到这一节,功能其实已经跑通了。但真正把这套代码拿去复用的时候,会遇到一批看起来莫名其妙的问题。下面这四个是我自己踩过并且在别人代码里也见过多次的。
6.1 0x80 以上根本不是 ASCII
很多教程会画一张 0 到 255 的表也叫"ASCII 表",实际上 128 到 255 这一段属于扩展编码,历史上不同厂商有不同定义,同一段码位在不同编码里对应的字符完全不一样。0x80在 Latin-1 里是控制字符,在 CP437 里是Ç,在 GBK 里是某个汉字的第一个字节。把这段当 ASCII 用,跨平台必出问题。
所以代码里的范围我严格限定在range(128)。如果确实需要看扩展部分,单独开一个函数,并且明确在表头标注"扩展编码,非 ASCII",避免自己以后看混。这个边界意识比多记几十个字符重要得多。
6.2 中文字符传给 ord,返回值大得超出预期
跑ord('中')得到 20013,第一次见会觉得程序坏了。这不是 bug,因为 Python 3 的字符串是 Unicode 序列,ord返回的是 Unicode 码位,不是字节值。判断一个字符是不是 ASCII,正确写法是ord(ch) < 128或者更直白的ch.isascii()(这个方法从 3.7 开始有)。
如果你的场景是"过滤掉所有非 ASCII 字符",用ch.isascii()比ord(ch) < 128可读性更好,而且它处理空字符串时会抛TypeError(因为空字符串不是单个字符),算是一种隐式的参数校验。反过来,如果要判断一个字节是不是 ASCII,注意bytes上取出来的是整数,直接b < 128就行。
6.3 CSV 用表格软件打开全是乱码
生成 CSV 的时候如果不指定编码,或者指定了utf-8但没加 BOM,用某些表格软件双击打开会看到一堆乱码。原因是这些软件在打开无 BOM 的 UTF-8 文件时会按系统默认编码(简体中文环境通常是 GBK)去解析。
解决办法是写文件时用encoding="utf-8-sig",这个编码名会在文件开头自动加上 BOM 标记,软件读到就知道是 UTF-8 了。另外记得加newline="",否则 Windows 上每行末尾会多出一个空行,原因是csv模块自己会写\r\n,而文本模式的换行转换又加了一层。两行代码解决:
import csv def export_csv(rows, path="ascii_table.csv"): with open(path, "w", encoding="utf-8-sig", newline="") as f: w = csv.writer(f) w.writerow(["DEC", "HEX", "OCT", "BIN", "字符", "字面量", "类别"]) for code, display, literal, kind in rows: w.writerow([code, f"0x{code:02X}", f"0o{code:03o}", f"{code:07b}", display, literal, kind])utf-8-sig对不需要 BOM 的读取方也没影响,Python 自己读的时候会自动跳过 BOM,是个两头都安全的写法。
6.4 用 Pillow 生成图片时,字体路径是最大的变数
有人喜欢图片格式,方便发到群里。Pillow的ImageDraw.text如果不指定字体,用的是内置的极简位图字体,小到看不清,而且不支持中文。指定ImageFont.truetype("DejaVuSansMono.ttf", 16)在 Linux 上通常能找到,但在某些系统上直接报OSError: cannot open resource。
稳妥的做法是写一个候选路径列表,逐个try,全失败就退回默认字体并降低预期(只输出 ASCII 部分,不写中文表头):
from PIL import ImageFont FONT_CANDIDATES = [ "DejaVuSansMono.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "/System/Library/Fonts/Menlo.ttc", "C:/Windows/Fonts/consola.ttf", ] def load_font(size=16): for p in FONT_CANDIDATES: try: return ImageFont.truetype(p, size) except OSError: continue return ImageFont.load_default()另外算画布尺寸的时候,别忘了等宽字体不等于每个字符宽度相同——比例字体才是主流。DejaVuSansMono这类等宽字体每个字符宽度一致,可以用font.getlength("M")乘字符数来估算;如果换成了非等宽字体,就必须逐字符累加宽度,否则文字会超出边界被裁掉。
7. 表生成完了,剩下的是把它变成"不用查"的东西
生成速查表是手段,不是目的。真正的目标是让自己在写代码时少查表。这里分享三个我用了几年的记忆锚点,配合上面生成的表看,效果比硬背强得多。
7.1 三个锚点:0x30、0x41、0x61
记住三个值就够了:0x30是字符0,0x41是A,0x61是a。其他的全从这个推:'7'是0x30 + 7即0x37;'F'是0x41 + 5即0x46;'z'是0x61 + 25即0x7A。三个锚点覆盖了数字、大写、小写三大高频区域,剩下的标点靠相对位置记。
十进制这边对应的锚点是 48、65、97。我个人的习惯是十六进制用于看协议和内存,十进制用于快速心算,两套都记一下,切换的时候不卡。
7.2 用位运算反推,比查表快
要把小写转大写,chr(ord(ch) & 0xDF);大写转小写,chr(ord(ch) | 0x20)。这两个操作在做字符串归一化的时候比str.upper()更底层,也更能说明"为什么大小写可以互转"。
判断一个字符是不是字母,可以用(ord(ch) | 0x20)落在0x61到0x7A之间来判断,这样一次比较同时覆盖大小写,比写两个区间判断简洁。当然实际项目里用ch.isalpha()更清楚,位运算的写法主要用在性能敏感或者需要展示原理的场合。
7.3 把脚本装成随手可用的命令
最后一步是把ascii_table.py放到一个在PATH里的目录,或者加一个 shell 别名。这样在写代码卡住的时候,敲一个短命令就能调出表来,不用去开浏览器:
# ~/.bashrc 或 ~/.zshrc alias ascii='python3 ~/tools/ascii_table.py --fmt grid' alias asciih='python3 ~/tools/ascii_table.py --fmt html --open'加一个--open参数用webbrowser.open()自动打开生成的 HTML,两行代码的事。工具链里这种"一次投入、长期回本"的小脚本,性价比比很多大项目都高。
我个人在实际操作中的体会是,这类生成类脚本最容易被写成一次性代码——跑出结果就扔了,下次需要又重写一遍。真正让它产生价值的是那两个额外步骤:把控制字符字典抽成独立模块方便复用,把导出格式做成参数化方便扩展。三分钟能写出第一版,但让它变成你工具箱里长期存在的东西,靠的是后面那半小时的整理。上面这些代码拼起来不到两百行,值得花时间落一次盘。