现场是这样的。清洗脚本用 re.sub 去页脚,模式里带了 .* 又没有关掉 DOTALL,结果把两条样本连同中间的换行一起吃掉,8 万条变成 7.1 万条。
中间走过弯路,把「替换没有限制次数,反复匹配到文末」当成主因,改完现象没变。
排查方向其实有限,先把可能性排一排。正则默认不跨行,但脚本显式开了 DOTALL;模式里的贪婪匹配没有加边界;清洗后没有做条数与长度分布校验。
先统计清洗前后的条数与长度分布
python -c "print(sum(1 for _ in open('before.txt')), sum(1 for _ in open('after.txt')))"
然后把贪婪匹配换成非贪婪并加行锚点
python -c "import re;print(re.sub(r'^页脚.*$','', '页脚 x', flags=re.M))"
随后关掉 DOTALL 后重跑并比对条数
python clean.py --in before.txt --out after_v2.txt --no-dotall
等前面都确认过,再按长度分桶确认没有超长合并样本
python len_stat.py --file after_v2.txt --buckets 100,500,2000
别只看报错消失,验证要多做一步。清洗前后条数只差 3%,长度分布没有出现异常长尾,抽查的 30 条样本边界完整。
这里还有个坑。跨行匹配最容易在清洗里制造隐形数据丢失,条数和长度分布是最便宜的哨兵,每次跑完都该看一眼。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。
