TXT重复字查找与清理:Word正则Python实战指南
1. 先分清四种“重复”方案才不会用错前阵子我帮人整理一本网络小说TXT全文上百万字里面光是“的的”“了了”这种连续重复字就出现二百多处。用眼睛一行行看看到凌晨也看不完。后来我写了个小脚本把连续重复字、叠字和重复行一次性扫出来不到一分钟就出了完整清单。这篇就把整套从手工到脚本的玩法摊开讲包括每个方案的适用范围和容易踩的坑。但动手之前一定要先分清你说的“重复”到底是哪一种。因为“txt查找连续重复的字”“删除重复的文字”“查找叠字”这三件事对应的工具和正则写法差别很大用错了不是删不干净就是把正常内容误删。重复类型典型例子常见处理方向连续重复字“的的”“了了”“哈啰哈啰”压缩成单字、保留N个字叠字/叠词“青青”“悠悠”“高高兴兴”通常是修辞只查不改连续重复词语“快乐快乐”“重要重要”删成单个词语连续重复行整段章节重复、日志连续相同行按行去重、合并流水一个很容易被忽略的点是叠字本身不一定是错别字。“青青子衿悠悠我心”里的“青青”“悠悠”是正常修辞不该被一键删掉。所以正规做法应该是“先扫描定位再人工确认哪些要处理”而不是上来就全局替换。另外文件大小直接决定工具路线。几百KB的文本用Word或Notepad手工处理完全够用几十MB甚至几百MB的TXT用记事本打开都费劲更别说全局查找替换。按我自己的经验通常这样选小于几百KB、只是想处理几个已知错误字用 Word/WPS 查找替换。会一点正则、文件在几十MB以内用 Notepad最快最直观。文件大、需要批量处理多个txt直接用Python脚本一次性读入扫描、输出清单、生成清洗后的文件。2. 用Word/WPS做小范围替换适合“已知错误”而不是“未知扫描”很多人第一步想到的是把TXT内容复制进Word用查找替换。这个方法不是不行但只适合你已经知道要改哪些字的情况。2.1 已知“的的”“了了”用普通查找替换就够了打开Word或WPS按CtrlH进入替换界面查找内容输入“的的”替换为“的”然后逐次点击“全部替换”。注意在替换前先点一次“查找全部”通过左下角的“找到 XX 项”确认实际数量避免文档里恰好有“目的的士”这类不该动的内容。实际操作中我建议把常见错误字列一个清单像“的的、了了、是是、就就、能能、哈哈、呵呵”一次性查完。但这里有一个Word处理中文叠字的硬伤它没法用通配符表达“任意连续两个相同汉字”。Word通配符里的?代表任意一个字符但??匹配的是任意两个字符不保证两个字符相等。所以你想搜索所有叠字Word做不到至少做不到像Notepad那样一个正则全扫出来。2.2 WPS和Word的小技巧用“查找全部”当扫描器虽然Word不能匹配“任意叠字”但你可以利用它做分类统计。先在查找框输入“的的”点击“查找全部”底部会列出所有命中位置点击任意一条就能跳到上下文快速判断是否要改。这个方法在文本不大时很好用因为人工判断的成本很高Word列表能帮你一屏看几十处。如果文件有上百万字Word打开就卡而且“全部替换”一旦点错撤销栈跟不上容易把源文件弄乱。所以我处理大TXT时最多用Word处理前几千字的样本确定替换规则后还是转到脚本或正则编辑器。2.3 为什么我不建议用Word处理整本小说Word的查找替换本质是面向文档编辑不是面向纯文本批量清洗。TXT文件超过10MBWord打开就很吃力超过50MB很容易假死。更重要的是普通TXT没有Word那种段落样式约束你替换错一个正则很难通过“撤消”找回大文件被批量改动的内容。如果只是偶尔改几个已知重复字Word是零学习成本的选择但如果目标是“把整个目录下的txt全部扫描一遍”Word的效率和组织性都不够。我的结论很简单Word适合你已经有明确目标文本时做定点修改不适合探索式扫描。3. Notepad正则匹配一次找出所有连续重复字如果你还没装Notepad建议先装一个。它是免费软件打开大TXT比记事本流畅得多而且自带PCRE正则查找替换处理“连续重复字”这类问题非常顺手。3.1 核心正则(.)\1在Notepad里按CtrlF打开搜索勾选“正则表达式”。查找内容(.)\1这段正则的意思(.)先捕获任意一个字符\1表示“和刚才捕获的那个字符相同”表示至少再重复一次。点“全部查找”结果面板会列出所有命中项。比如文本里有“哈哈”“的的”“111”它都会找出来。注意这里的.默认不会跨行所以不会把行尾和下一行开头匹配到一起这是好事。如果你只想找中文叠字不想误伤英文里的ll、数字里的11可以稍微改一下查找内容([一-龥])\1这个正则的[一-龥]表示从汉字“一”到“龥”的Unicode范围基本覆盖常用汉字。这样will里的ll不会被命中中文叠字照常能查出来。3.2 替换规则压缩成1个还是保留2个扫描之后如果决定替换按CtrlH查找模式选“正则表达式”。如果想把所有连续重复字压缩成一个比如“的的”变“的”、“哈哈哈哈”变“哈”查找内容(.)\1替换为\1如果想把连续重复字保留两个比如“哈哈哈哈”变“哈哈”、“的的的”变“的的”查找内容(.)\1{2,}替换为\1\1这里要特别解释一下(.)\1{2,}它匹配的是“同一个字符连续出现3次及以上”所以只有两字叠词不会被碰。很多人在这一步栽跟头以为用(.)\1然后替换成\1\1就能保留两个实际不行——那是把重复串全部替换成两个字符但如果原文只有两个重复字也会被替换成两个看着没差别。真正干净的写法是上面这种。3.3 匹配中文重复词语或短句连续重复的词语通常不是单字而是两个或更多字组成的词比如“快乐快乐”“好的好的”“你来了你来了”。Notepad可以用下面这个正则查找内容([一-龥]{2,6})\1替换为\1这个正则捕获2到6个汉字然后要求后面再出现一遍同样内容。点“全部查找”时你会发现“快乐快乐”被整段命中替换后就只剩下一个“快乐”。但要注意这个写法很容易误匹配到“东南西北西北风”这种恰好包含重复片段的句子。所以中文重复词我从来不做全局替换都是先“全部查找”看一遍命中列表再决定是否单独替换。还有如果重复词中间有标点比如“快乐快乐”上面的正则就匹配不到因为中间多了标点。这时候可以换成查找内容([一-龥]{2,6})[。!?,.\s]?\1这个写法允许中间夹一个标点或空格但不要贪心把[。!?,.\s]?改成*否则它可能跨过很长一段文本去匹配极易误伤。4. Python脚本批量扫描、定向删除、整目录处理如果文件超过几十MB或者你有一整个文件夹的TXT要处理Notepad手工点击效率就太低了。改用Python脚本几分钟就能跑完。这里的思路依然是“先扫描、后清洗”避免脚本自动删掉不该删的内容。4.1 先扫描叠字和连续重复字把下面代码保存成scan_duplicates.py和你要处理的demo.txt放在同一目录然后在命令行运行import re def scan_duplicate_chars(text, min_repeat2, max_hits300): # min_repeat2 表示查找连续重复2次及以上的字符 pattern re.compile(r(.)\1{%d,} % (min_repeat - 1)) hits [] for m in pattern.finditer(text): start max(0, m.start() - 10) end min(len(text), m.end() 10) context text[start:end].replace(\n, \\n) hits.append((m.group(), m.start(), context)) return hits[:max_hits] if __name__ __main__: with open(demo.txt, encodingutf-8) as f: text f.read() for dup, pos, ctx in scan_duplicate_chars(text): print(f{pos:8d} {dup!r:10} ...{ctx}...)运行后输出每一条命中的位置、重复内容和上下文。我一般只打印前300条避免控制台被刷爆。如果命中的是“的的”你可以继续往下看如果命中“哈哈”这种语气词你心里就有数知道不能全局删。4.2 清洗脚本连续重复字、重复词、重复行确定规则后再用清洗脚本。以下这个clean.py会依次做三件事把连续汉字重复压缩为单字删除连续重复词语删除相邻重复行。import re def reduce_hanzi_repeats(text, keep1): # keep1: 连续重复的汉字只保留1个 # keep2: 连续重复的汉字最多保留2个 if keep 0: return text pattern re.compile(r([\u4e00-\u9fa5])\1{%d,} % keep) return pattern.sub(lambda m: m.group(1) * keep, text) def remove_consecutive_phrases(text, min_len2, max_len6): # 中文词语或短句连续重复只保留第一段 pattern re.compile( r([\u4e00-\u9fa5]{%d,%d})(?:[\s。!?,.]*\1) % (min_len, max_len) ) return pattern.sub(r\1, text) def remove_adjacent_duplicate_lines(text): # 相邻两行内容相同只保留第一行 lines text.splitlines() out [] prev None for line in lines: key line.strip() if key ! prev: out.append(line) prev key return \n.join(out) def read_text_auto(path): # 自动尝试常见编码优先处理Windows下常见的UTF-8/GBK for enc in (utf-8-sig, utf-8, gb18030): try: with open(path, r, encodingenc) as f: return f.read() except UnicodeDecodeError: continue with open(path, r, encodingutf-8, errorsignore) as f: return f.read() if __name__ __main__: text read_text_auto(demo.txt) # 按需开关不是每一个都要执行 # 把两个以上重复汉字缩成一个 text reduce_hanzi_repeats(text, keep1) # 把“快乐快乐”这类连续重复词删成单个 text remove_consecutive_phrases(text) # 删除相邻重复行 text remove_adjacent_duplicate_lines(text) with open(demo_clean.txt, w, encodingutf-8-sig) as f: f.write(text)read_text_auto这个函数解决了很多Windows用户最常见的编码问题。老式TXT很多是ANSI/GBK编码直接用UTF-8读会抛错或产生乱码。这里按utf-8-sig、utf-8、gb18030的顺序尝试基本能覆盖绝大多数中文TXT文件。写入时用utf-8-sig也就是带BOM的UTF-8这样Windows记事本打开不会乱码。4.3 批量处理整个目录下的TXT如果只有一个文件运行脚本就够了。但我实际用到最多的场景是“某文件夹里十几个小说txt全都要清理”。这时把上面的处理函数和下面的批量逻辑合到一起from pathlib import Path def clean_file(src): text read_text_auto(src) text reduce_hanzi_repeats(text, keep1) text remove_consecutive_phrases(text) text remove_adjacent_duplicate_lines(text) out src.with_name(src.stem _clean src.suffix) with open(out, w, encodingutf-8-sig) as f: f.write(text) return out if __name__ __main__: for path in Path(.).glob(*.txt): if path.name.endswith(_clean.txt): continue print(正在处理:, path.name) clean_file(path)这里有个细节批量处理时输出文件名加_clean后缀保留原始文件不动。我第一次写脚本时直接原地覆盖结果正则考虑不周整个文件被改坏原始数据又没备份只能重新下载。从那以后我的所有文本清洗脚本默认都是“写新文件”。5. 编码、备份、正则误伤最容易拖垮进度的三个坑这一节不是废话而是我踩过最多的地方。很多人不是不会找叠字是前脚搜出来后脚就把整个文件搞得乱码或者误删。5.1 编码是TXT处理的第一道坎TXT没有任何格式信息同一个文件可能是UTF-8、UTF-16 LE、ANSI/GBK。用Notepad打开时如果中文显示成乱码先别急着替换在“编码”菜单里切换“转为ANSI”“转为UTF-8”看效果。有一个容易混淆的概念菜单里有“以UTF-8编码打开”和“转为UTF-8编码”两种选项。前者是重新读文件后者是把当前内存内容转换编码后保存。如果你文件已乱码直接“转为UTF-8”只会把乱码状态固定下来。正确做法是先尝试不同编码打开看到正常中文后再做后续操作。在Python里gb18030比gbk更通用它能兼容几乎所有GBK字符还覆盖生僻字。所以我脚本里面的编码尝试顺序是固定的utf-8-sig在前gb18030兜底。5.2 备份比多写十行代码更有用做任何批量替换前先复制一份备份。最简单的做法copy demo.txt demo.txt.bak或者把整个文件夹打包。用Notepad时虽然可以CtrlZ撤销但如果文件几十MB撤销一次要等很久而且Notepad不一定能完整恢复。用脚本清洗时脚本也没有可交互的撤销机制。所以我自己养成了一个习惯凡是正则替换和代码清洗永远先扫后改凡是批量修改永远先备份。5.3 正则误伤到底有多常见(.)\1看着简单实际误伤率极高。英文文本里will会被它匹配成ll重复替换后变成wil数字文本里119911会被层层匹配标点符号里连续感叹号也会被匹配。还有一类隐蔽误伤是中文本身。比如“明明”是正常词但如果你在处理“明明明明白白我的爱”这类歌词或口语文本正则很可能把三个字以后的内容压缩掉直接破坏原文。这就是为什么我不建议任何人跳过“扫描清单”直接“全部替换”。用([一-龥])\1这类汉字限定正则能规避一部分英文和数字误伤但语义层面的误伤只能靠人眼确认。另一个常见错误是替换方向搞反。很多人想删除重复文字结果把查找内容写成\1替换成(.)\1等于把所有单字变成重复越换越多。检查一下方向确保“查找内容”是正则“替换为”是反向引用这个低级错误通常就不会犯。6. 扩展用法从叠字词频到小说水字清理这套方案不只用于改正错别字我后来把它扩展到了几个实际工作场景效果都不错顺手分享出来。6.1 叠字词频统计如果你需要统计一部作品里所有叠字出现的频率可以用这段代码import re from collections import Counter text read_text_auto(demo.txt) counter Counter(m.group() for m in re.finditer(r([\u4e00-\u9fa5])\1, text)) for word, count in counter.most_common(30): print(word, count)注意([一-龥])\1匹配的是两个相同汉字连在一起像“青青”“悠悠”“明明”都会命中然后按出现次数排序。做诗词或语料分析时这个列表能直接告诉你叠字风格集中在哪几个字上。6.2 小说TXT的“水字”清理组合拳网络小说下载文件里常见两类污染一是同义章节标题重复出现二是整段重复的“水字”。对章节标题我会先用Notepad扫描第.{1,6}章之类的重复行对整段重复我会用remove_adjacent_duplicate_lines做一次相邻去重。这样做的原因是整段重复通常都是紧挨着的不会误删全书其他位置同样会出现的正常句子。6.3 日志文件压缩如果你处理的不是中文TXT而是服务器日志重复行会连续刷屏。remove_adjacent_duplicate_lines同样适用但更聪明的做法是把连续重复的次数记下来而不是直接删除。改一行代码就能实现在判断key prev时给计数器加一不输出重复内容等遇到新行时再把上一行的重复次数附在行尾。这样日志从几十万行压缩到几千行信息量一点不少。最后分享一个我个人坚持了很多年的习惯所有文本清洗任务第一次运行永远是在副本上做。等输出文件确认无误再把原文件覆盖掉。这个习惯一开始看起来有点笨但只要你手滑误删过一次就会明白它有多值钱。

相关新闻

千问    LeetCode 307.区域和检索 - 数组可修改 Java实现

千问 LeetCode 307.区域和检索 - 数组可修改 Java实现

LeetCode 307 题「区域和检索 - 数组可修改」是一道经典的数据结构设计题,要求实现一个类,支持单点更新和区间求和两种操作。 为什么不能用普通前缀和? 如果使用前缀和数组,sumRange 查询是 O(1),但 update 更新一个元…

2026/10/12 6:22:43 阅读更多 →
UE5 UMG图表插件实战:纯C++绘制曲线、饼图、环状图与柱状图

UE5 UMG图表插件实战:纯C++绘制曲线、饼图、环状图与柱状图

简介:这是一款面向Unreal Engine5开发者的UMG图表控件插件,专为游戏与虚拟现实项目提供纯UMG实现的数据可视化方案,无需WebBrowser或WebUI嵌套,通过C与蓝图即可完成图表搭建。插件覆盖曲线图、饼图、环状图与柱状图四类常用图表&a…

2026/10/12 6:22:43 阅读更多 →
零基础实战Vibe Coding:普通人用AI从0到1做出小工具

零基础实战Vibe Coding:普通人用AI从0到1做出小工具

最近一年被问得特别多的一句话是:"我完全没写过代码,现在让 AI 帮我做个小工具,到底靠不靠谱?"以前听到这种问题,我一般会说你先去补点基础再谈。但这两年 Vibe Coding 这个概念火起来以后,我的答…

2026/10/12 6:22:43 阅读更多 →

最新新闻

DeepSeek开源算力地基:FlashMLA与DeepEP如何加速国产大模型?

DeepSeek开源算力地基:FlashMLA与DeepEP如何加速国产大模型?

先说明一下我的第一反应:看到“致敬,DeepSeek 最新开源的不是模型,是国产算力的地基”这个标题,我以为是又一个大模型权重开源了。结果点进仓库一看,里面没有模型文件,躺着的全是 FlashMLA、DeepEP、DeepGE…

2026/10/12 7:08:08 阅读更多 →
NumPy随机函数与广播机制:维度对齐、代码实战与踩坑全解

NumPy随机函数与广播机制:维度对齐、代码实战与踩坑全解

【day 48】随机函数与广播机制,打卡到这个组合的时候,我明显感觉身边不少同学开始卡壳了:随机函数单独用没问题,广播规则单独看也能理解,但真到写代码的时候,要么随机生成的数组形状对不上,要么…

2026/10/12 7:08:08 阅读更多 →
MTCNN人脸检测详解:P-Net、R-Net、O-Net三级网络训练与部署实战

MTCNN人脸检测详解:P-Net、R-Net、O-Net三级网络训练与部署实战

简介:面向深度学习和计算机视觉研究者,这份完整代码基于MTCNN级联卷积网络实现人脸检测,清晰覆盖P-Net候选框提议、R-Net边界框精修、O-Net关键点定位的完整流程。包体共80个文件,以Python源码为核心,包含48个py文件&a…

2026/10/12 7:08:08 阅读更多 →
物联网通信技术选型与架构设计:从链路预算到协议栈的工程实践指南

物联网通信技术选型与架构设计:从链路预算到协议栈的工程实践指南

干了十几年通信,最明显的感觉是:打电话的人越来越少,问"能不能帮我把这个箱子里的定位数据传回来"的人越来越多。物联网这个概念被炒了很多年,别人看到的是智能家居、智慧城市、大风口,我看到的是另一件事—…

2026/10/12 7:08:08 阅读更多 →
MyBatis查询功能全解析:从动态SQL到性能优化的实战指南

MyBatis查询功能全解析:从动态SQL到性能优化的实战指南

项目标题: MyBatis各种查询功能用到一定年头你就会发现,MyBatis的查询功能说难不难,说简单也绝不是写个select标签就完事。日常开发里,动态条件、分页、多表嵌套、批量操作,任何一个场景都能写出好几种不同写法,而每种…

2026/10/12 7:08:08 阅读更多 →
老毛桃UEFI启动盘v7.0:稳过Secure Boot,原生支持NVMe与Win11架构识别

老毛桃UEFI启动盘v7.0:稳过Secure Boot,原生支持NVMe与Win11架构识别

简介:老毛桃U盘启动盘制作工具(UEFI版 装机版)v7.0是一款面向电脑初学者与系统维护人员的轻量级系统辅助工具,专为快速制作兼容UEFI与传统BIOS的U盘启动盘、安装原版Windows系统及执行PE环境下的故障排查而设计。资源包共2个文件&…

2026/10/12 7:07:08 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →