使用方法
- 把文本粘进输入框。默认示例是一段含重复的 11 行代码:同一行出现 2~3 次,还混着空行、带首尾空格的行和全大写变体。
- 选判等口径。不确定就先用「严格全等」——它只删逐字节相同的行,最安全。
- 点「删除重复行」或按 Ctrl+Enter(Mac 上 Cmd+Enter;Enter 本身留给换行)。
- 输出区给出保留结果,统计条报出「输入几行、保留几行、删了第几行」;重复次数超过 1 的行可在重复统计里核对。
- 「复制结果」带走输出;「导出重复统计 CSV」下载每个重复行的出现次数与首次出现行号(带 BOM,Excel 不乱码);「载入示例」随时还原演示数据。
这个删除重复行工具能解决什么
日志去重是最典型的场景:同一个异常栈一晚上刷几千次,删重后只剩每种一条,但行尾的不可见字符会让肉眼相同的两行被判成不同——比如从 Windows 日志粘来的行尾带回车符、从 Excel 粘来的行首带 BOM。本页的四种口径就是为了让你显式选择「什么算同一行」。
代码清单去重是第二类:import 语句重复、依赖列表里同一包写了两次。此时「忽略大小写」口径要慎用——Python 的 import pandas 与 IMPORT PANDAS 在严格口径下是两行,判等后合并可能改变语义。
词表、邮箱列表、优惠券码清洗是第三类:先删重复行再交给后续流程,重复统计表能告诉你哪个值重复了多少次、第一次出现在哪一行,方便回溯上游为什么重复写入。
本页不做的事:不删空行(那会改变语义,需要时用删除空行);不合并相邻重复段;不做模糊近似判等——「看起来差不多」的两行永远按不同行处理。
原理与规则说明
删除重复行的判等核心是 Set.prototype.has/add:遍历每一行,先算「判等键」(按所选口径规范化),键没见过就保留并记录,见过就跳过。Set 迭代按插入序,天然保留首次出现位置,不需要额外的序号数组。这是 O(n) 哈希判等;用 indexOf 在结果数组里找重复则是 O(n²)——本机实测 5000 行全部唯一时 indexOf 要 22.46 ms 而 Set 只要 0.42 ms(54 倍),2 万行差 173 倍,5 万行差 614 倍(9.1 秒对 15 毫秒),10 万行起 indexOf 已经不可用。
切行必须写 split(/\r?\n/)。写成 split('\n') 时 CRLF 文件的每行尾部会留一个 \r,「b\r」与「b」判不等,重复删不掉;单独的 \r(旧 Mac 换行)也会把两行粘成一行。末尾换行还有第二坑:'a\n'.split('\n') 得到 ['a', ''],多出一个空串。本页的处理是——原文以换行结尾就 pop 掉这最后一个空串,不以换行结尾就保留;空行本身是真实内容,不算换行符的产物。
四种口径的规范化函数依次是:原样(严格全等);trim()(ECMA-262 的 WhiteSpace 产生式,会顺手清掉 U+00A0、U+FEFF 和各类 Zs 空格);再加 toLowerCase();再加 /[\u200B-\u200D\u2060]/g 清零宽格式字符。注意 trim 清不掉 U+200B/200D/2060——它们是 Cf 类格式字符,不在 WhiteSpace 清单里,所以第四个口径才存在。
BOM 的危害不在「整表都带 BOM」(那种情况判等口径一致,反而正常),而在只有部分行带 BOM:表头手写、数据行从 Excel 粘贴时,「id」与「\uFEFFid」逐字节不同,严格口径下 4 行输入会判出 4 个唯一值,重复完全没被识别;切到去首尾空白口径,trim 把 U+FEFF 一并清掉,立刻回到 2 个。
完整示例(数值仅作演示)
删除重复行的默认示例是 11 行文本,严格全等口径:保留 7 行、删除 4 行,删的是第 4、5、9、10 行。逐行看——第 1 行 import pandas as pd 保留,第 4 行与它逐字节相同被删;第 3 行 df.head() 保留,第 5、10 行被删;第 6 行空串也是一行,第 2 次出现才删,这里只出现一次所以保留;第 7 行「 df.head() 」带首尾空格,严格口径下是第 2 个唯一值,第 8 行 IMPORT PANDAS AS PD 大小写不同照样保留。
同一份输入切换口径:去首尾空白保留 6 行(第 7 行的键变成 df.head(),撞上第 3 行);再去大小写保留 5 行(第 8 行也撞上第 1 行);再清零宽字符仍是 5 行——演示数据里没有零宽字符,口径 2 与 3 在这份输入上答案相同,这正是「口径要对数据敏感」的直观演示。
另一个七行样本可以拉开四档口径的差距:df.head()、带 BOM 的同名行、NBSP 开头的行、两侧空格的行、尾部带零宽空格的行、单字符 A、再来一行 df.head()。严格口径 6 个唯一,trim 口径 3 个,加忽略大小写仍 3 个,再清零宽只剩 2 个。
输入范围与对照表
| 判等口径 | 规范化函数 | 默认示例结果 | 典型场景 |
|---|---|---|---|
| 严格全等 | 原样(键 = 行本身) | 保留 7 行 / 删 4 行 | 代码、配置,字节级一致才算重复 |
| 去首尾空白 | trim() | 保留 6 行 / 删 5 行 | 手工录入、Excel 粘贴(顺带清 BOM) |
| 忽略大小写 | trim().toLowerCase() | 保留 5 行 / 删 6 行 | 邮箱、域名、URL 清单 |
| 清零宽字符 | trim + 清 U+200B/200D/2060 再忽略大小写 | 保留 5 行 / 删 6 行 | 从网页、PDF 反复复制粘贴的文本 |
不可见字符对照表见工具区:前 8 行会被 trim 清除(WhiteSpace 或 Zs 类),后 4 行不会被任何口径自动清除,除非选「再清零宽字符」。
输入范围、边界与精度说明
规模上限 20 万行:Set 构建 20 万个字符串键实测 108.9 ms,文本约 2.3 MB,加上 Set 结构的额外内存,普通设备无压力;超过后本页不做截断,但浏览器标签页内存吃紧时可能卡顿,建议分段处理。空输入是合法输入:0 行进、0 行出、删除 0 行,不报错。
边界行为逐条定义过:全部行相同时唯一 1 行、删除 n−1 行;单行无换行时原样返回、不补换行符;输入只有一个空行时输出为空文本——这是正确结果(那一个空行被保留了),不是故障。行内容永远按字符串处理,不做数字类型转换,文本世界里不存在「1 与 1.0 相等」。
常见误用的正确解释:第一,删完比预期多一行,多半是末尾换行产生的空串被当成了一行——本页已按「原文是否以换行结尾」处理,其他工具未必;第二,strict 口径删不掉 CRLF 文件的重复,是 \r 留在行尾,先选去首尾空白口径即可;第三,去重不等于排序,首次出现顺序就是输出顺序,需要字典序请用按字母排序行。
常见问题
删重复行之后顺序乱了吗?
没有。Set 按插入序迭代,输出顺序 = 每个值首次出现的顺序。要与原文件「先排序再去重」的结果一致,先过打乱行工具反而会破坏这个性质,注意区分。
为什么两个看起来一模一样的行删不掉?
多半是行里藏着不可见字符:行尾 \r(CRLF 文件)、行首 BOM、零宽空格。对照表里逐个列了码点;先切「去首尾空白」口径,仍不行再切「再清零宽字符」。
空行会被删掉吗?
只有空行出现两次及以上时才删多余的那个——空串也是一个合法的行值。想把空行全部清掉是另一个需求,交给删除空行工具。
20 万行以上会怎样?
本页不设硬拒绝,但 Set 构建实测 20 万行 108.9 ms,几十万行级别内存与耗时线性上涨,建议先按 20 万行拆分。五万行量级用 indexOf 写法要 5 秒以上,本页不用那种实现。
忽略大小写对中文有意义吗?
没有。toLowerCase 只影响拉丁字母与部分扩展字符,中文行在口径 2 与口径 1 下结果相同;对中英混排的清单,它只合并英文部分的差异。
重复统计里的「首次出现行号」从几开始数?
从 1 开始,与编辑器状态栏一致。行号按切行后的顺序计,末尾换行不产生额外行号。
CSV 为什么带 BOM,而输出文本不带?
Excel 依赖 BOM 识别 UTF-8,不带会乱码;但 BOM 粘进文本文件会变成行首 U+FEFF,正好制造本页要清除的那种幽灵差异,所以只给 CSV 加。
输入会传到服务器吗?
不会。判等只用 Set 与字符串方法,全程在浏览器本地执行,Network 面板可见零请求;刷新页面输入即消失。
相关工具
- 行统计:只看行数、空行数、最长行等指标,不动内容。
- 按字母排序行:去重后经常接的下一步,支持正逆序。
- 打乱行工具:做抽样或测试数据时打乱顺序。
- 删除空行:本页刻意不做的另一件事,专职清空行。
- 删除多余空格:把行内的连续空格压成一个,与本页的首尾空白口径互补。
- 换行符转逗号转换器:把行清单压成 CSV 行,去重后导出的常见接续。
- 添加行数字转文本:给每行加序号,核对首次出现行号时好用。
更多文本处理工具见分类页。