TXT指定行删除:sed、Python与图形化方案实操指南

TXT指定行删除:sed、Python与图形化方案实操指南 手上有几百兆的 txt 文本翻到中间发现混进了几十行脏数据用鼠标一行行选中再按退格删到第三行就眼花一不小心还多删了两行——这个场景我遇到过不止一次。后来才明白删除 txt 等文本文档中指定行本质上是一个定位 过滤 回写的问题跟你用什么软件关系不大关键是先把指定行这三个字定义清楚是按行号删还是按内容特征删还是按某种规则删。搞不清这一点工具越花哨越容易出事。这篇内容我打算把手上这些年处理文本文档的经验一次说透从命令行到脚本从编辑器到表格软件把删除行这件事拆成可复现的步骤。不管你是刚接手数据清洗任务的新人还是常年跟日志、配置、小说文本打交道的老手都能从里面挑出一套直接能用的方案。1. 先把指定行定义清楚需求拆解与方案选型1.1 四类场景工具选错等于白干删除指定行听起来是一件事实际上至少分四类每一类适配的工具完全不同。第一类是按行号删比如第 1 到第 3 行是表头删掉第 500 到 520 行是一段测试数据删掉。这类需求定位精确行号是唯一依据处理起来最快但也最危险——只要源文件被人动过一行行号就全错位。第二类是按内容特征删比如删掉所有空白行删掉所有以 # 开头的注释行删掉带广告两个字的行。这类需求不依赖行号容错性高是日常最常用的方式。第三类是按条件删判断依据不是内容本身而是行的属性比如删除长度小于 5 个字符的行删除逗号分隔后字段数不等于 6 的行删除重复出现的行。第四类是批量删一个目录下几百个文本文件每个都要执行同样的删除规则这时候就必须走脚本或命令行手动点开一个一个改是不可能的。我见过太多人把这四类混在一起处理明明是批量场景却用编辑器一个个打开明明是复杂条件却硬要用正则凑。结果就是时间花了不少质量还不稳定。所以在动手之前先花两分钟把需求写到纸面上——删哪些、依据是什么、涉及几个文件、能不能接受重新生成一个新文件——这四个问题回答完工具基本就自动浮现了。1.2 六种方案横向对照选之前先看这张表下面这张表是我自己整理过的每次接手新任务都会先扫一眼。表里的风险等级指的是误删后能否快速恢复不是指操作难度。方案最适合的场景上手成本误删风险是否原地修改编码兼容性手动编辑器编辑几十行以内、零散删除极低中是好sed / awk中大型文件、规则固定中高不备份是一般字节流PowerShellWindows 环境、无需装环境中中可控制好可指定Python 脚本批量、复杂规则、需可复现中低输出新文件可选最好Notepad / VS Code千行级、交互式调整低中是好Excel / WPS结构规整、可视化核对低低否中受表格限制选型的判断顺序我一般是这样走的第一步看文件编码和换行符如果是 GBK 或者混着 CRLF 和 LF优先选 Python 或 PowerShell因为这几类工具能显式指定编码sed 在 Windows 环境下处理中文 UTF-8 相对省心但遇到 GBK 就容易出乱码。第二步看规模一千行以内用编辑器最舒服能看到上下文十万行以上一定要走脚本或命令行编辑器加载就要等半天。第三步看要不要可重复如果这个删除动作以后每周都要做一次那就别偷懒老老实实写个脚本存下来第二次用的时候能省下大量时间。还有一个容易被忽略的点是否需要保留原始文件。sed 加-i是原地改的改完原文件就没了Python 默认可以写到一个新文件里出问题还能对比。我自己现在的习惯是只要不是临时看一眼的活儿一律输出到新文件确认没问题再覆盖。2. 命令行三板斧sed、awk、PowerShell 直接改文件2.1 sed 按行号删除单行、区间、离散多行sed 是处理文本行最顺手的工具删除动作只用一个d命令。删单行最典型# 删除第 5 行原地修改 sed -i 5d data.txt # 删除第 2 行到第 10 行含两端 sed -i 2,10d data.txt # 删除最后一行 sed -i $d data.txt注意2,10d是闭区间第 2 行和第 10 行都会被删掉一共删 9 行。这个点我第一次用的时候也踩过以为不含尾行结果多理解了一层反而不对。如果要删的是好几段离散的行比如第 3 行、第 7 到第 9 行、第 15 行可以用多个-e串起来sed -i -e 3d -e 7,9d -e 15d data.txt这里有个非常关键的细节sed 在执行多个删除命令时是按行流式处理的每读一行就依次尝试所有命令。所以这些行号是相对于原文件的行号不会因为前面的删除而导致后面的行号偏移。换句话说写行号的时候直接照抄你在编辑器里看到的位置就行不需要删完前一段要减掉几行这种换算。这一点比很多新手自己写的脚本要省心得多。如果你用的是 macOSsed -i后面必须跟一个空字符串参数写成sed -i 5d data.txt否则 sed 会把后面的表达式当成备份文件后缀名报错或者生成一个奇怪的文件。这个坑在跨平台脚本里特别常见建议脚本里加个系统判断。2.2 sed 按内容匹配删除正则的边界要拿捏准实际工作中按行号删的比例其实不高更多是按内容匹配。常见的几种写法我整理成了一张对照# 删除所有空白行含只有空格和制表符的行 sed -i /^[[:space:]]*$/d data.txt # 删除以 # 开头的行 sed -i /^#/d data.txt # 删除包含测试两个字的行 sed -i /测试/d data.txt # 删除包含测试或临时的行 sed -i -E /测试|临时/d data.txt # 反向删除只保留包含订单的行其余全删 sed -i /订单/!d data.txt最后一条!d是反向操作意思是不匹配这个模式的行才删除等价于只留下匹配的行。在做数据提取的时候特别好用——与其一条条列要删什么不如直接说清要留什么逻辑更短、出错更少。这里必须提醒一个高频事故/^[[:space:]]*$/d里的[[:space:]]不能写成\s。虽然 GNU sed 支持\s但 BSD sedmacOS 默认不支持脚本换到另一台机器上就跑不通了。另外如果文件里的空白行其实含有零宽字符或者 BOM 残留正则也匹配不到这时候要先用cat -A data.txt | head看一眼不可见字符确认清楚再写规则。2.3 awk 的等价写法适合条件更复杂的场合awk 和 sed 能力有重叠但 awk 在按行号加条件组合的时候表达力更强而且默认不修改原文件需要自己重定向安全性反而更好。# 保留除第 5 行以外的所有行 awk NR ! 5 data.txt data.clean.txt # 删除第 2 到第 10 行保留 1 行和 11 行之后的 awk NR 2 || NR 10 data.txt data.clean.txt # 同时删掉第 3 行、第 7~9 行 awk NR ! 3 !(NR 7 NR 9) data.txt data.clean.txt # 删除字段数不等于 6 的行以逗号分隔 awk -F, NF 6 data.txt data.clean.txt最后一条特别值得说它是按条件删的典型。NF是 awk 的内置变量表示当前行按分隔符切开后的字段数量。文本数据在传输或复制过程中很容易出现字段丢失导致某几行少了一个逗号用NF 6直接把不合规的行筛掉比人眼检查快得多。如果你还想保留不合规行的记录把条件反过来加上单独输出到另一个文件就得到了一份异常行清单方便后续追因。2.4 PowerShellWindows 上不装任何东西也能干很多 Windows 用户机器上没有 Git Bash 也没有 WSL这时候 PowerShell 是最省事的选择。按行号删除的写法$path .\data.txt $lines Get-Content -LiteralPath $path -Encoding UTF8 $drop (5, 8, 9) # 要删除的行号从 1 开始 $keep for ($i 0; $i -lt $lines.Count; $i) { if (($i 1) -notin $drop) { $lines[$i] } } Set-Content -LiteralPath $path -Value $keep -Encoding UTF8这段代码里有两个容易翻车的地方我第一次写的时候都踩了。第一个是下标问题数组$lines是从 0 开始的而人说的第几行是从 1 开始的所以判断时要写($i 1) -notin $drop少加这个 1 就会删错相邻的一行而且删完看起来好像也对不仔细核对根本发现不了。第二个是编码问题Windows PowerShell 5.1 的Set-Content -Encoding UTF8会写出带 BOM 的文件而 PowerShell 7 写的是不带 BOM 的。如果你的下游程序对 BOM 敏感很多解析器会把 BOM 当成内容的一部分导致第一行多出三个不可见字节就要显式指定编码对象$utf8NoBom New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllLines((Resolve-Path $path), $keep, $utf8NoBom)按内容删除在 PowerShell 里靠Where-Object# 删除空白行和以 # 开头的行 $keep $lines | Where-Object { $_.Trim() -ne -and $_ -notmatch ^# }需要注意的是Get-Content默认会把整个文件读进内存对于几十兆的日志文件还能扛到了几百兆就可能吃满内存。真要处理超大文件还是转回 Python 流式读取更稳妥。3. Python 脚本方案可控性最强适合批量与复杂规则3.1 按行号集合删除为什么要一次性过滤而不是循环删新手最容易写出的代码是这样的先读所有行到列表然后 for 循环遍历行号列表每找到一个就pop一次。这个写法有致命缺陷——pop之后列表长度变了后面所有元素的下标全部前移除非你从大到小倒序删否则一定删错。我更推荐的做法是一次性过滤用一个集合存要删的行号遍历原文件时判断当前行号是否在集合里不在就写到新文件。逻辑清晰复杂度是线性的也不存在下标错乱的问题# delete_lines_by_number.py # 用法python delete_lines_by_number.py 输入.txt 输出.txt 5 8 9 20-30 import sys def parse_ranges(tokens): 把 5 20-30 这样的参数解析成一个行号集合 drop set() for t in tokens: if - in t: a, b t.split(-, 1) drop.update(range(int(a), int(b) 1)) else: drop.add(int(t)) return drop def delete_by_number(src, dst, drop): with open(src, r, encodingutf-8, errorsreplace, newline) as fin, \ open(dst, w, encodingutf-8, newline) as fout: kept 0 for i, line in enumerate(fin, start1): if i in drop: continue fout.write(line) kept 1 print(f共删除 {len(drop)} 个行号位置实际写入 {kept} 行) if __name__ __main__: src, dst sys.argv[1], sys.argv[2] drop parse_ranges(sys.argv[3:]) delete_by_number(src, dst, drop)这个脚本有两个细节是我反复打磨出来的。第一是newline它让 Python 不做换行符转换原文件是什么样输出就是什么样。如果不加这个参数Python 在 Windows 上会把\n统一转成\r\nLinux 上又会转回去最后文件的字节数和原来对不上用 diff 一比对全是差异。第二是errorsreplace遇到编码不干净的字节时不会直接抛异常中断而是替换成占位符保证整个流程能跑完。当然代价是那一行内容会失真所以更严谨的做法是先确认编码再处理。命令行用法很直观python delete_lines_by_number.py raw.txt clean.txt 1-3 500-520表头三行加上一段脏数据一起删掉参数写起来比一串 sed 表达式更好读也更容易在团队里交接。3.2 按关键词与正则删除一次覆盖整目录内容匹配版本我一般会写成一个规则列表把所有要删的模式集中在一处改规则不用改逻辑# clean_texts.py import re from pathlib import Path DROP_PATTERNS [ re.compile(r^\s*$), # 空白行 re.compile(r^\s*#), # 注释行 re.compile(r广告|推广|扫码关注), # 推广内容 re.compile(r^\s*(第\s*\d\s*页|Page\s\d)\s*$, re.I), # 分页残留 ] def should_drop(line): return any(p.search(line) for p in DROP_PATTERNS) def clean_file(src: Path, dst_dir: Path): dst dst_dir / src.name total dropped 0 with src.open(r, encodingutf-8, errorsreplace, newline) as fin, \ dst.open(w, encodingutf-8, newline) as fout: for line in fin: total 1 if should_drop(line): dropped 1 continue fout.write(line) return total, dropped, total - dropped if __name__ __main__: src_dir Path(./raw) dst_dir Path(./clean) dst_dir.mkdir(exist_okTrue) for f in sorted(src_dir.glob(*.txt)): t, d, k clean_file(f, dst_dir) print(f{f.name}: 原始 {t} 行删除 {d} 行保留 {k} 行)这套写法的价值在于规则集中。团队里几个人共用一份脚本时谁要加一条新的删除规则只需要往DROP_PATTERNS里加一行正则不用理解整个流程。我见过一些脚本把正则散落在十几个地方半年后连原作者都不敢改。3.3 大文件流式处理内存占用要算清楚上面这段代码有一个天然的优势它是逐行读、逐行写不会把整个文件塞进内存。这一点在处理大文件时非常关键。假设一个 txt 有 200 万行平均每行 100 个字符全读进内存大约是 200 MB 的字符串数据再算上 Python 字符串对象本身的开销实际占用可能是原始大小的两到三倍也就是四五百兆。如果同时处理十个文件机器就开始卡了。流式读取的开销是常数量级的无论文件多大内存里同时只有一行。代价是不能做跨行的判断比如删除重复行或者删除前面出现过同样内容的行这种需求就必须要保留一个已见行的集合。这时候要权衡如果重复率很低集合本身可能比文件还大可以考虑用哈希值代替原文存储把内存占用压下来。import hashlib seen set() with open(src, r, encodingutf-8, errorsreplace, newline) as fin, \ open(dst, w, encodingutf-8, newline) as fout: for line in fin: key hashlib.md5(line.strip().encode(utf-8)).hexdigest() if key in seen: continue seen.add(key) fout.write(line)用 16 字节的 MD5 值代替动辄上百字节的整行内容集合的内存占用能降一个数量级。哈希碰撞的概率在 MD5 下对于百万级数据几乎可以忽略但如果你对准确性有极端要求可以换成 SHA-256代价是计算稍慢。3.4 编码自动识别别让 GBK 文件把你坑了国内的老系统导出的 txt很大比例是 GBK 编码甚至有的文件前面几行是 GBK、后面混进了 UTF-8 的片段。直接按 UTF-8 打开会抛UnicodeDecodeError。我的处理方式是用一个候选编码列表逐个尝试取第一个能被完整解码的def read_all_lines(path, candidates(utf-8-sig, utf-8, gbk, gb18030)): for enc in candidates: try: with open(path, r, encodingenc, newline) as f: return f.readlines(), enc except UnicodeDecodeError: continue raise RuntimeError(f无法识别编码{path})这里我特意把utf-8-sig放在第一位。因为带 BOM 的 UTF-8 文件用utf-8打开时第一行开头会多出一个不可见的\ufeff字符很多解析器会因此判定第一行格式错误。用utf-8-sig打开会自动把这个 BOM 吃掉省去后面一堆莫名其妙的第一行为什么匹配不上的排查。gb18030放在最后是因为它是 GBK 的超集兼容性最好用它兜底基本能覆盖绝大多数中文文本。需要注意的是这种试解码的方式只在文件较小时好用因为它要把整个文件读一遍。对于动辄几百兆的文件更靠谱的做法是用专门的探测库只采样前几 KB 来推断编码然后再决定用什么打开。4. 不写代码的图形化方案编辑器与表格软件4.1 Notepad 正则替换删行Notepad 是 Windows 上处理文本的老牌工具删行这件事它有两种思路我都常用。思路一是正则替换。按CtrlH打开替换窗口找到模式选正则表达式然后在查找目标里写^.*测试.*\r?\n替换为留空点全部替换所有包含测试的行连同换行符一起消失。这里的\r?\n是关键它同时兼容 Windows 的 CRLF 和 Unix 的 LF。如果只写\n在 Windows 文件上替换完会留下一堆空行看起来删了一半。思路二是书签法这个技巧知道的人不多但特别好用。操作顺序是CtrlF打开查找切到标记标签输入关键词勾选标记所在行点全部标记这时候目标行左侧会出现蓝色小圆点。然后走菜单搜索 → 书签 → 删除已标记行一整批行就干净地消失了。这个方法的好处是不涉及正则纯字符串匹配对正则语法不熟的人也能一次成功。我处理小说 txt 里的推广行时基本都走这条路把书友群加群公众号分别标记一遍最后一次性删除。4.2 VS Code 的多光标与正则替换VS Code 的强项是交互式编辑。如果目标行是连续的选中之后直接CtrlShiftK就能整行删除比按退格再删换行快得多。如果是分散的十几行可以按住Alt逐行点击造出多个光标然后一次性按CtrlShiftK。行数不多的时候这种手动方式比写正则还快而且所见即所得不会误删。正则替换的入口是CtrlH点开右侧的.*图标启用正则写法跟 Notepad 类似但要注意 VS Code 的替换为里$0表示整个匹配留空即可。有一个细节值得注意VS Code 默认的.不匹配换行所以^.*关键词.*$只能匹配到行尾不会跨行相对安全。如果你不慎写了带[\s\S]的贪婪表达式可能一口气匹配掉半个文件替换前最好先点查找看看匹配数量对不对。还有一个很实用的场景批量修改文件。VS Code 在工作区搜索里支持替换能一次性对多个文件执行同样的删除规则效果等价于第 3 节里的批量脚本但不用写代码。文件数量在几十个以内、规则又比较直观的时候这个方案的时间成本最低。4.3 Excel / WPS 辅助法先编号再筛选文本本身行数不多、结构又比较规整的时候表格软件反而是最安全的方案因为所有内容都摊在眼前能看见再删。操作流程是这样的把 txt 复制到 Excel 的一列里比如 A 列在 B 列填公式ROW()生成行号然后根据需求加一列判断比如IF(ISNUMBER(SEARCH(测试,A1)),删,留)接着对判断列做筛选只显示删选中这些行整行删除最后把 A 列数据复制回 txt 文件即可。这个方案的优势是可视化和可核对而且不存在正则写错了批量误删的风险——Excel 会在删除前让你看到有多少行将被删掉。缺点是受单元格限制Excel 单元格最多只能放 32767 个字符单行内容特别长的日志会溢出另外表格会自动做类型识别像00123这种看起来像数字的编码会被自动去掉前导零把 Excel 列格式设成文本再粘贴能避免这个问题但细节多了容易忘。4.4 图形化方案的适用边界说到底图形化方案适合行数在一万以内、规则简单、需要人眼确认的场景。一旦超过这个量级编辑器会开始卡顿滚动条拉一下要等两秒人在这种环境下反而更容易出错。再往上就必须交给命令行或脚本。判断的分界线我自己的标准是删除规则用一句话能说清楚并且目标行可以用关键词准确描述就交给脚本如果规则需要一边看一边调整就用编辑器。最怕的是拿不准规则又硬上脚本反复改参数跑了七八次最后自己都不知道哪次的结果是对的。5. 实操全流程演示一次 12 万行编码数据的清理5.1 任务背景与文件体检前阵子接手的一批数据是某系统导出的商品编码 txt共 12.4 万行要求是删掉开头三行表头、所有空白行、所有含测试或TEST字样的行、以及第 8000 到 8120 行这一段明显错位的脏数据。文件原始编码未知需要先做体检。体检我用三步。第一步看大小和行数ls -lh data.txt wc -l data.txt输出显示 45 MB、124038 行行数在脚本的舒适区内45 MB 用流式处理毫无压力。第二步看编码和换行符file -i data.txt # 查看 MIME 编码Linux/macOS head -c 200 data.txt | xxd | head -10 # 看开头字节判断是否有 BOM结果显示charsetutf-8开头三字节是ef bb bf也就是 UTF-8 BOM。这就解释了为什么下游程序总说第一行格式错误——BOM 被当成了内容的一部分。所以后续脚本必须用utf-8-sig打开。第三步抽查内容sed -n 1,3p data.txt sed -n 8000,8005p data.txt sed -n 124036,124038p data.txt这里有个小技巧sed -n start,end p只看指定区间比在编辑器里滚动快得多。抽查确认了第 8000 行开始的一段内容确实是另一个表的结构混进来了而且没有明显的分隔标记只能按行号删。首尾也都确认了没有多余的空行残留。5.2 分步执行与结果校验体检完我把删除规则拆成两组按行号的和按内容的。顺序很重要我先处理行号再处理内容。原因在 2.1 节说过——sed 和 awk 的行号是基于当前输入流的如果先删了内容行后面的行号就会整体前移第 8000 到 8120 行指的就不是原来那段数据了。所以顺序必须是先锁定行号再按内容过滤或者干脆在同一个脚本里用原文件行号做判断Python 方案天然满足这一点。最终的脚本把两组规则合到一起用原文件行号加内容规则同时判断import re from pathlib import Path SRC Path(data.txt) DST Path(data.clean.txt) DROP_LINES set(range(1, 4)) | set(range(8000, 8121)) # 表头 脏数据段 CONTENT_RULES [ re.compile(r^\s*$), re.compile(r测试|TEST, re.I), ] def should_drop(idx, line): return idx in DROP_LINES or any(p.search(line) for p in CONTENT_RULES) with SRC.open(r, encodingutf-8-sig, newline) as fin, \ DST.open(w, encodingutf-8, newline) as fout: total dropped 0 for i, line in enumerate(fin, start1): total 1 if should_drop(i, line): dropped 1 continue fout.write(line) print(f总行数 {total}删除 {dropped}保留 {total - dropped})跑完输出总行数 124038删除 291保留 123747。符合预期——3 行表头 121 行脏数据 167 行内容匹配加起来 291。校验环节我做了三件事。第一行数核对wc -l data.txt data.clean.txt第二随机抽样对比确认没删错行diff (sed -n 1,5p data.txt) (sed -n 1,2p data.clean.txt)第三确认残留规则确实被清干净grep -c -iE 测试|TEST data.clean.txt grep -c -E ^[[:space:]]*$ data.clean.txt两条都返回 0说明目标行确实删干净了。5.3 保留回滚能力这一步千万别省上面这套流程里我全程没有动原文件所有输出写到data.clean.txt。有人觉得 45 MB 的文件占地方改完直接删原文件省事我强烈不建议。校验通过之后再删成本是一样的但在校验之前原文件就是你唯一的退路。如果非要原地修改至少做一次备份并且把备份和结果放到不同目录cp data.txt backup/data.txt.$(date %Y%m%d%H%M)用带时间戳的文件名避免第二次备份把第一次覆盖掉。这个习惯是我在被一次误操作教育之后养成的——当时批量脚本跑错了一个目录几十个文件被清了内容好在上一轮的备份还在才没造成更大损失。另外一个实用建议是把每次清理的规则和结果统计记到一个日志文件里比如clean.log内容包括时间、输入文件名、删除行数、使用的规则版本。半年后有人问这份数据为什么少了两百行翻日志比翻代码快得多。6. 高频问题排查实录与速查表6.1 删了行但文件看起来没变这是问得最多的一个现象。打开文件一看目标行还在或者空白行删完之后多出来一片空白。绝大多数情况是换行符没处理干净。如果删的是一行内容但没删对应的换行符那一行就会变成空行留在原地。比如用编辑器替换^.*测试.*$只匹配了内容没匹配\n结果就是一片空行。正确的做法是把换行符一起吃掉^.*测试.*\r?\n。反过来如果删除的是空白行但文件里那些空白行实际上包含了空格、制表符或者全角空格正则^$就匹配不到。这时候要用^[[:space:]]*$但全角空格\u3000不在[[:space:]]里还得单独加进去。判断方法很简单把文件用cat -A data.txt | head -50输出行尾的$前面如果能看到^I制表符或者一堆空格就说明不是真空白行。第三个可能是 sed 的-i在 macOS 上没生效反而生成了一个data.txt-e之类的备份文件你以为改的是原文件其实改的是它的副本。检查目录里有没有多出来的奇怪文件即可。6.2 中文变成乱码了乱码几乎都出在编码不匹配上。用 sed 或 awk 处理 GBK 文件时因为它们按字节流操作中文的每个字占两个字节如果正则里含有中文字符匹配可能命中半个字导致输出文件里出现乱码。这个问题的根治办法是不要在 GBK 文件上用带中文的正则转成 UTF-8 再处理iconv -f GBK -t UTF-8 data.txt data.utf8.txt处理完如果需要交回原系统再转回去iconv -f UTF-8 -t GBK data.clean.txt data.clean.gbk.txtPython 方案相对安全因为它在解码后是按字符处理的中文字符是一个整体。但要注意写回时用的编码必须和指定的一致读的时候是utf-8-sig、写的时候是utf-8BOM 会被丢掉——这通常是好事但如果下游程序依赖 BOM 来判断编码就要写回utf-8-sig。6.3 常见问题速查表现象最可能的原因快速验证方法解决办法目标行删了但留下空行只匹配了内容没匹配换行grep -n ^$ 文件看空行位置正则末尾加\r?\n空白行删不掉行内含空格或全角空格cat -A 文件 | head改用^[[:space:]]*$并补全角空格中文乱码编码不匹配或按字节匹配中文file -i 文件先 iconv 转 UTF-8 再处理第一行总是匹配不上有 BOM 头head -c 3 文件 | xxd用utf-8-sig打开或先剥 BOM行号删错了相邻一行数组下标没加 1对比删除前后该行内容行号转下标时统一加 1输出文件字节数变多换行符被统一转换ls -l对比大小读写时加newline大文件处理时内存爆掉一次性 readlines观察内存占用改逐行流式处理sed 在 mac 上没生效-i需要空参数看目录是否多出备份文件写成sed -i 5d脚本报 UnicodeDecodeError文件是 GBK 或混合编码尝试用 GBK 打开候选编码逐个尝试解码6.4 备份、校验、日志三条硬规矩最后想说三条我自己一直在守的规矩看起来是小事但真正出过一次事故之后就知道值不值。第一条任何删除操作之前先备份。这不是以防万一的心理安慰而是把误操作的成本从数据永久丢失降到重跑五分钟。备份文件名带时间戳不要图省事用data.bak第二次执行就把第一次覆盖了。第二条删除之后一定做数量核对。保留行数加上删除行数应该等于原文件总行数这个等式对不上就说明脚本在某处重复计数或者漏读了行。这个校验几乎不花时间但能拦住绝大多数隐性错误。更进一步随机抽十行对比原文和结果比从头到尾肉眼扫一遍高效得多。第三条把规则写进脚本而不是写在命令行里。命令行里敲sed -i -e 3d -e 7,9d很爽但过两周你自己都想不起来当时删了哪几行。写成脚本文件规则集中在一处加上注释说明每一段为什么删这份东西就变成了可复用的资产下次遇到同类任务改两个参数就能直接跑。我个人的体会是删除指定行这件事的技术难度其实很低真正的难点在于证明你删对了。工具只是手段能不能拿出行数核对、抽样对比、规则日志这三样东西才是区分数得清的清理和凭感觉的瞎删的分界线。