名单去重了还有重复?批量数据清理的五个坑
从 Excel、日志、聊天记录里粘出来的名单,肉眼看着干净,去重时总有残留。原因几乎都在「看不见的差异」上 —— 判重规则、字符形态、以及排序的码位比较。
- 判重的单位是整行,两行去掉首尾空白后完全相同才算重复,不做相似度判断,也不忽略标点或词序
- 去重的比较与输出都会先 trim,所以粘带缩进的内容会丢结构,代码、YAML 这类文本不适合用它整理
- 普通排序按 UTF-16 码位比较,大写字母排在小写字母前面,所以 Apple 会跑到 apple 前面
- 自然排序把每行切成数字段与非数字段交替比对,item2 因此排在 item10 前面,而普通排序正好相反
- 中文排序既不按拼音也不按笔画,而是按 Unicode 码位,要拼音序得先把文本转成拼音再排
#先分清是哪一种「重复」
「去了重还有重复」其实分两种情况:一是判重规则没覆盖到那些差异,二是重复根本不在同一行里。 混在一起处理,改哪个工具都不对。
判据很简单:把两条疑似重复的行复制出来,逐字符对比。
| 现象 | 真正的原因 | 怎么处理 |
|---|---|---|
| 两条看着一样却都保留 | 尾部空格、全角字符、大小写不同 | 先 trim 与全角统一,再判重 |
| 同一个人出现两次但写法不同 | 信息不在同一行(如「张三 138…」与「张三」) | 需要按字段拆分,工具做不到 |
| 去重后行数没变 | 每行都不同(时间戳、序号导致) | 先去掉无关列再判重 |
| 空行也被算进去了 | 去空白行没勾 | 确认「去空白行」是勾选状态 |
#整行判重:什么算重复
判重的单位是整行:两行去掉首尾空白后完全相同才算重复。 它不做相似度、不忽略标点、不忽略词序 —— 「苹果 香蕉」和「苹果香蕉」是两条不同的行。
用 文本去重 时,三个行为决定了结果:
- 比较和输出都会先 trim,行首行尾的空格被去掉
- 顺序按首次出现固定,不会重排成字母序
- 勾选忽略大小写后,输出保留第一次出现时的大小写:
Apple在前就输出Apple,apple在前就输出apple
也就是说,它的输出是一条「首次出现顺序 + 原始大小写」的干净列表。想按字母或数字排,接着用排序工具。
「去空白行」默认是勾选的,所以粘进来的首尾空行会直接被吃掉。粘贴带缩进的内容(代码、Markdown 列表、YAML)时更要小心 —— 缩进会被 trim 掉,结构跟着就变了。这类文本不要用去重工具整理。
#去重会动你的缩进
trim 是去重的必要步骤(否则尾部空格会造成假重复),代价是缩进信息丢失。 这个取舍对名单类数据完全正确,对代码类数据完全错误。
判断标准:这段文本的缩进有语义吗?
- 名单、标签、ID、URL 列表:缩进无语义,放心用
- 代码、YAML、Markdown 列表、SQL:缩进就是语法,用完就坏了
代码类文本要去重,应先在编辑器里做,或用支持「忽略缩进但保留缩进」的工具。这也是为什么通用文本工具永远替代不了格式化工具 —— 两者关心的东西不同。
#排序:为什么 A 在 a 前面
普通排序按 UTF-16 码位逐个比较,而大写字母的码位比小写字母小(A 是 65、a 是 97),所以 Apple 会排在 apple 前面。 这不是 bug,是码位比较的必然结果。
用 文本排序 时的三种情况:
- 想要大小写不影响顺序:勾「忽略大小写」
- 中文:既不按拼音也不按笔画,而是按 Unicode 码位(大致等于该字在字符表里的位置)。要拼音序得先把文本转成拼音再排
- 排序是稳定的:两行排序键相同时保持原相对顺序,不会被随机打乱
排序键取的是每行 trim 后的内容,输出也是这个版本 —— 行首行尾空格会消失。取消勾选「去空白行」后,空行按空字符串处理,升序时会全部挤到最前面。
顺序建议:先去重再排序。反过来也能得到结果,但重复行会挤在一起占位置,看起来乱;先去重时「保留首次出现」的语义也更符合直觉。
#自然排序解决什么
普通排序把 item10 排在 item2 前面,因为它逐字符比较,1 小于 2。人类习惯的读法正好相反 —— 这就是自然排序存在的理由。
自然排序先把每行切成「连续数字段」和「非数字段」交替比对,数字段按数值大小比:
| 行 | 普通排序 | 自然排序 |
|---|---|---|
| item2 | 第 2 位 | 第 1 位 |
| item10 | 第 1 位 | 第 2 位 |
| v1.9 | 在 v1.10 之后 | 在 v1.10 之前 |
版本号、页码、带序号的商品、章节标题都该用自然排序。而纯文本姓名、地址用普通排序更合适 —— 里面没有数字段,两种方式结果一样。
#批量替换:正则剩下的活
去重和排序只处理「行」,行内的改动要靠替换。 这在清理从网页复制的表格时特别常见:多出来的制表符、括号里的备注、统一的前缀。
# 去掉行尾的「(已停用)」这类备注
查找:\s*(已停用)$
替换:(留空)写正则时的三个高频坑:
.不匹配换行:跨行要加s标志或用[\s\S]*?^与$默认不是逐行:多行模式要加m\d不认全角数字:从 Excel 粘来的123匹配不到,先做全角转半角
替换内容里的 $ 永远是特殊字符(与是否正则模式无关),要输出字面的美元符号得写 $$。
替换是单次遍历,不会对结果再跑一遍。所以 A→B、B→A 这类互换不会死循环,但也一步做不完 —— 要先把 A 换成中间占位符,再换第二次。
#什么时候该用哪个工具
| 你想做的事 | 用哪个 | 关键差异 |
|---|---|---|
| 去掉重复的名单行 | 文本去重 | 整行判重,会 trim 掉缩进 |
| 按字母或数字排序 | 文本排序 | 码位排序,大写在前 |
| 版本号、页码按人眼顺序排 | 文本排序 | 选自然排序,数字段按数值比 |
| 颠倒行序或字符序 | 文本反转 | 四种模式,多行要选行内反转 |
| 确认到底有多少行、多少词 | 文本统计 | 按码点计数,区分字符与字节 |
| 验证一个替换模式对不对 | 正则表达式测试 | 支持 g/i/m/s/u 修饰符 |
| 执行批量替换 | 查找替换 | 普通模式自动转义元字符 |
清洗流程建议:统计行数(知道基线)→ 全角转半角 → 去空白行与去重 → 需要时排序 → 行内用替换收尾。 每一步都保留中间结果,出问题能定位到具体一步。
#常见问题
#两条一模一样的行为什么都留下来了?
先逐字符对比,最常见的是行尾多了空格或制表符。去重时虽然会 trim,但如果你看到的是「去重前」的样子,差异可能在其他地方:全角半角(123 与 123)、不间断空格( 粘进来是 U+00A0,看起来和空格一样)、或者零宽字符。用 文本统计 看字符数能快速判断。
#中文排序为什么不是按拼音?
因为它按 Unicode 码位排,码位与拼音无关。要做拼音序,需要先把每个字转成拼音(这需要专门的中文处理库),再按拼音字符串排序,最后映射回原文。浏览器原生排序做不到这件事。
#去重之后行数反而变多了?
检查「显示计数」是否被勾选 —— 它会在每行前面加「次数 + 两个空格」,输出已经不是纯数据了。把带计数的结果再拿去当输入,会被当成新的一批行重新判重。
#想删掉所有空行,用什么?
去重工具默认就会去空白行。如果只想删空行而保留重复行,用 查找替换 的正则模式:查找 ^\s*$\n?,替换留空。多行模式要记得勾。
#反转之后中文变成乱码了?
说明用错了模式。字符反转会把换行也一起倒排,导致行与行错位;而且它按码位拆,由多个码位组合的内容(带肤色的手势、用零宽连接符串起来的家庭 emoji)会散开。多行文本要「每行倒着写」请选行内反转。
本文提到的工具
文本去重在线工具,按行快速去重,支持忽略大小写与空白行过滤,可显示每行出现次数,保留首次出现顺序,数据…
立即使用 →文本排序文本排序在线工具,支持升序、降序与数字感知的自然排序,可忽略大小写与过滤空白行,按行快速整理数据,数据…
立即使用 →文本反转文本反转在线工具,支持字符反转、行反转、行内反转与单词反转四种模式,中文与 emoji 安全处理,实时…
立即使用 →正则表达式测试正则表达式测试在线工具,实时匹配高亮与语法错误提示,支持 g/i/m/s/u 修饰符、分组捕获显示、常…
立即使用 →文本统计文本统计在线工具,实时统计字符数、字数、行数、段落数与字节数,支持中文按字英文按词统计,高频字符频率分…
立即使用 →