☰
用Python批量提取txt文件内容,自动生成目录索引文档
2026/9/26 17:49:56 网站建设 项目流程

大概一个月前,朋友把他攒了多年的电子书和文档备份发给我,说想整理出一份清单。我打开那个文件夹一看,好家伙,两千多个txt文件,文件名从“笔记01”到随手打的“未命名168”都有。人肉一个个打开看根本不现实。我花了二十分钟写了个小脚本,干的事核心就一句话:提取txt文档名称和内容前N个字符,生成新的目录文档。效果立竿见影——上午跑完,下午他就能对着目录找东西了。

这个需求听起来简单,但处理文本时那些边边角角的问题,折腾了我一下午才稳定下来。如果你也需要在大量txt文件里建立索引、做预览、整理语料,这篇文章可以帮你少踩不少坑。下面这套代码和思路,我用它处理过小说备份、论文资料、爬虫语料,算是实测过很多轮了。

1. 我的使用场景:哪些人真的需要这个“txt目录生成器”

1.1 资料归档与快速预览

我接触到的第一类典型场景,是个人资料归档。很多人的电脑里都有一堆txt,读书笔记、摘抄、小说片段、工作日志,时间一久文件名会变得毫无规律。与其一个个文件打开看,不如生成一份目录文档,把每个文件的名称、路径和内容摘要放在一张表里,一眼扫过就知道这个文件大概是什么。

这类用户通常不需要复杂的功能,只要脚本能递归遍历文件夹,把文档名称、路径、内容前N个字符按行输出到一个新文件里就够了。目录文档本身就是一个可搜索的索引,配合VS Code或Notepad++的多文件搜索,效率能再翻倍。我朋友拿到目录后,直接在编辑器里搜“龙族”“笔记”“合同”,几秒钟就把需要的文件定位到了。

1.2 语料筛选与数据集整理

第二个场景是我自己遇到的。做自然语言处理或者准备微调数据时,经常要快速判断一个语料文件是否符合要求。比如我想把一批小说txt转成统一的对话格式,先得知道每个txt的开头长什么样。这时候如果手动去翻阅,几百个文件就够人退缩了。

用这个脚本把每个文件的内容前100个字符提取出来生成目录文档,我就能在编辑器里用关键词筛选出“对话体”“第一人称”等特征,先做一个粗糙的预筛,再决定哪些文件进数据清洗流程。说白了,它不是个成品工具,而是整个数据流水线的第一道过滤器。目录文档帮忙省下的时间,比我写脚本的时间多得多。

1.3 内容合规检查与抽样

还有一种你可能没想过的用法:文本内容合规检查。比如从网上扒下来的一批txt,可能有广告头、乱码头或者重复的免责声明。通过目录文档快速抽样开头,马上能发现哪些文件头部是异常内容,质量怎么样。

甚至可以把脚本的参数改成N等于一个很大的值,也就是提取全文,输出成另一个清单,用来做重复检测。当然,这里要提醒一句:大规模抓取和整理网络文本时,务必注意版权问题,个人使用和备份没问题,但别拿去做传播。目录文档这个工具本身是中性的,怎么用取决于你。

2. 拆解核心逻辑:文件名、前N个字符、目录文档这三件事

2.1 目录文档应该长什么样

在写代码之前,先把需求拆成三个子问题:找文件,读内容,写目录。目录文档的设计直接决定了后续好不好用。我推荐用制表符分隔的表格样式:序号、文件相对路径、内容摘要三列。如果文件都在同一个目录下,也可以只保留文件名;但如果有多层子目录,建议保存相对路径,方便定位。

实际效果类似这样:

序号 文件相对路径 内容前100个字符 1 ./books/龙族.txt 这是一个关于少年与龙的故事... 2 ./notes/机器学习笔记.txt 今天学完决策树,终于理解了剪枝...

制表符的好处是能被大多数编辑器和Excel无缝识别。如果你要长期维护这个目录,我建议加一行表头,并固定字段顺序,后续写脚本解析也很轻松。不要把内容里的换行保留,否则一行记录会变成多行,整个表格结构瞬间乱掉。

2.2 前N个字符的正确截取方式

这里有个新手容易踩的坑:“前N个字符”不等于“前N个字节”。在Python中,用open()读取时给一个整数N,比如f.read(100),是按Unicode字符读取的。中文、英文、标点都算一个字符,这正好符合我们提取摘要的需求。

但如果你在Linux下用head -c 100,它就是按字节截取,一个UTF-8汉字占3个字节,所以100字节只能取到33个汉字左右,还会截出半个字符。写脚本时一定要想清楚是按字符还是按字节。对于这个工具,按字符最直观,满足绝大多数场景。Python标准库直接按字符处理,所以不用担心多字节编码的问题。

2.3 遍历目录时需要注意的排序规则

文件夹里的文件列表,系统默认给的顺序往往不是按名称排列的,尤其是Windows资源管理器里的“按名称排序”,跟Python的sorted()字典序并不完全一样。比如文件“10.txt”会被排在“2.txt”前面,因为字典序先比较第一个字符,1小于2,所以“10”会在“2”前面。

这种细微差别在少量文件时无所谓,但文件多了,生成的目录文档顺序会显得很乱。我建议要么直接用os.walk拿到路径列表后做一下自然排序,要么干脆不做排序,按文件系统返回的顺序输出,同时在目录文档里注明“顺序不代表优先级”。我的习惯是使用成熟的自然排序函数,但为了不引入额外依赖,简单方案是文件名按小写排序,或者用正则提取数字前缀作为排序key。

3. 手把手实现:从零写一个可用的批处理脚本(Python)

3.1 环境准备与整体代码结构

先解释为什么用Python:标准库就能处理文件遍历、读取和写入,十几行代码搞定。下面的版本是我整理过的,加入了基本的容错和参数交互,你可以直接复制到本地运行。

环境只需要Python 3.6以上,不需要第三方库。脚本分四块:路径处理、文件收集、内容提取、目录输出。核心不复杂,重点是每一块都要考虑到真实世界里的脏数据。如果你连Python都没装,去官网下载一个,然后右键这个脚本用IDLE打开,按F5就能跑,非常简单。

3.2 完整脚本与关键逻辑注释

#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import sys import glob def collect_txt_files(root_dir, recursive=True): """收集目录下所有txt文件,返回绝对路径列表""" files = [] if recursive: for root, dirs, filenames in os.walk(root_dir): for fn in filenames: if fn.lower().endswith('.txt'): files.append(os.path.join(root, fn)) else: # 不递归,只处理当前目录 pattern = os.path.join(root_dir, '*.txt') files = glob.glob(pattern) # 按路径字符串排序,保证输出相对稳定 files.sort(key=lambda p: p.lower()) return files def read_head(file_path, n_chars): """读取文件前n个字符,并清理换行等空白符号""" text = None # 依次尝试常见编码,避免乱码 for encoding in ('utf-8', 'gbk', 'latin-1'): try: with open(file_path, 'r', encoding=encoding) as f: text = f.read(n_chars) break except UnicodeDecodeError: continue if text is None: return "[编码无法识别]" # 把换行、回车、制表符替换为空格,避免目录行被截断 text = text.replace('\r\n', ' ').replace('\r', ' ').replace('\n', ' ') text = text.replace('\t', ' ').strip() return text def build_catalog(files, n_chars, root_dir): lines = [] for idx, fp in enumerate(files, 1): rel = os.path.relpath(fp, start=root_dir) head = read_head(fp, n_chars) if head: lines.append(f"{idx}\t{rel}\t{head}") else: lines.append(f"{idx}\t{rel}\t[空文件或读取失败]") return lines def main(): root_dir = input("请输入要扫描的目录路径(留空为当前目录): ").strip() if not root_dir: root_dir = os.getcwd() n_chars_str = input("请输入要提取的字符数(默认100): ").strip() n_chars = int(n_chars_str) if n_chars_str.isdigit() else 100 files = collect_txt_files(root_dir, recursive=True) if not files: print("该目录下没有txt文件,退出。") return lines = build_catalog(files, n_chars, root_dir) # 注意:输出成.tsv而不是.txt,避免被脚本自己扫描到 out_path = os.path.join(root_dir, '目录文档.tsv') with open(out_path, 'w', encoding='utf-8', errors='replace') as f: f.write("序号\t文件相对路径\t内容前{}个字符\n".format(n_chars)) f.write('\n'.join(lines)) print(f"完成:共提取 {len(files)} 个txt文件,目录文档已写入 {out_path}") print(f"内容摘要按每个文件的前 {n_chars} 个字符截取。") if __name__ == '__main__': main()

几个关键点我要单独说一下:

  • collect_txt_files里用了os.walk,会把子目录里的txt也搜出来,适合整理嵌套很深的文件夹。如果你只需要当前目录一层,把recursive改成False就行。
  • read_head里尝试三种编码,UTF-8覆盖大多数现代txt,GBK覆盖Windows旧文件,latin-1是兜底,保证不会因为解码失败而中断整个目录生成。
  • 输出文件名用了目录文档.tsv,这样脚本在下次运行时不会把这个目录文档本身当成待整理的文件,避免了“目录里包含自己”的尴尬。

3.3 配置N值和路径的小技巧

我一般会把N值做成命令行参数而不是交互输入,这样方便批量调用。比如python make_catalog.py --path D:\txts --num 200。如果你不想写argparse,直接用sys.argv判断前两个参数就够了。

另外N值的选择直接决定目录文档的可用性。经验值是100到200个字符:100字符能表达大意的八分之一,足够判断文件主题;200字符更稳妥,但文件数量超过一千时,目录文档会膨胀到四五十万字符,打开和搜索依然很流畅。如果只想看文件名,N设成0就行,等着一行行文件名列表。

再说一个细节:目录文档的输出路径默认放在扫描目录下,如果输出后缀是.txt,下次扫描时会被当成普通文本处理。我改用.tsv后缀,以后想转Excel也方便。如果你坚持要.txt后缀,至少要写一行排除逻辑,把输出文件本身跳过。

4. 实测中的四个坑:编码、换行、超长文本和特殊文件名

4.1 编码问题:GBK与UTF-8的乱码战争

这是我在实际测试中遇到的第一大坑。Windows下很多人用记事本保存的txt默认是GBK或ANSI编码,而Linux和macOS下基本都是UTF-8。如果脚本只按UTF-8读取,GBK文件轻则输出乱码,重则直接报UnicodeDecodeError。

我最初的脚本用的就是utf-8 + errors='ignore',结果中文变成了一堆问号和乱码,完全不可用。后来加了编码回退逻辑:优先UTF-8,解码失败再用GBK尝试,最后用latin-1兜底。latin-1不会失败,但会把字节直接映射成奇怪的字符,至少能保留内容的原始结构,方便后续修复。

经验:如果文件都是自己用Windows记事本创建的,直接指定gbk反而更稳。更通用的做法是用chardet检测编码,但那样会增加一个依赖。我这版脚本只靠标准库,已经能满足大多数场景。如果你需要处理混合编码的目录,建议先用记事本打开几个看看,确定主力编码后改一下read_head里的顺序,能更快得到干净结果。

4.2 换行符和制表符把目录行搞乱了

第二个坑,是内容前N个字符里混着换行符。txt文件开头经常是空行或者标题换行,如果原样写入目录文档,一行记录就会变成多行,整个表格结构瞬间乱掉。

解决办法很简单:在提取到文本后,把换行、回车、制表符统一替换为空格。这里要注意顺序:Windows的换行符是\r\n,所以要先把\r\n替换成空格,再单独处理\r和\n,否则会出现两个空格。还可以顺便strip()去首尾空白。

我还遇到过一种情况:有些小说txt开头是连续多个空行,导致提取出的摘要只有空白。这时可以在替换后判断strip结果是否为空,如果是就标记为“文件开头为空”,而不是让那行看起来像缺失数据。这样目录文档的每一行都有明确信息,后续人工检查时心理负担小很多。

4.3 特殊文件名:空格、括号、Emoji和隐藏字符

第三个坑,是文件名本身。Windows文件名不允许反斜杠、冒号等,但允许方括号、井号、中文、Emoji。这些字符在控制台和目录文档里都没问题,但如果你把目录文档导入Excel,某些字符可能会被识别成公式,比如文件名以=开头的文件。

我的处理建议是:目录文档里统一用相对路径代替纯文件名,这样即使重名也不会丢信息;如果文件名里包含超长路径,Windows限制260字符,脚本应该捕获路径超长的异常,至少要打印出失败的路径。

我自己碰到过一次,某个txt文件名末尾有个不可见空格,导致Python打印出来看起来正常,但复制到其他地方找不到文件。所以最后还是坚持用相对路径作为唯一标识,而不是只看文件名。目录文档里保留相对路径还有一个好处:当你把整个文件夹打包压缩,目录文档跟着一起走,换个环境也能快速对应到原文件。

4.4 当N值设得过大,目录文档本身会爆炸

最后一个坑比较隐蔽。如果你把N设成10000想多看些内容,而且文件都是几十万字的小说,脚本会读取每个文件的前一万字符,生成超大的目录文档,打开和搜索都会变慢。

更合理的做法是设置上限:如果文件总字符数小于N,就只读全部;如果大于N,就读取前N。同时读取时最好用f.read(N)而不是f.read()再切片,后者会把整个文件读入内存,效率差很多。对于动辄几十MB的txt,用read(n)才是正确姿势。

另外提醒一句:目录文档生成后,最好别直接编辑保存,不然下一次脚本重新扫描时会把你的备注也提取进去。我通常把目录文档.tsv当作快照,备注写在另一个md文件里,这样即使重新生成目录,原来的备注也不会被覆盖。

5. 进阶扩展:从纯txt到目录文档的更多玩法

5.1 按时间或大小过滤,目录文档更干净

第一个能立刻让脚本更实用的扩展,是文件过滤。比如只提取最近修改的txt,或者只提取大于10KB的文件,避免把一堆只有几行的临时文件也收进来。

实现也不复杂,在collect_txt_files阶段增加条件判断:os.path.getsize(fp) >= min_size,或者用os.path.getmtime(fp)比较修改时间。按我的经验,把过滤条件做成参数后,目录文档的质量会高很多,真正有用的是那些大文件,小文本碎片通常可以直接排除。

5.2 同时输出Markdown和CSV,方便不同工具消费

我现在生成目录文档时,会同时输出两个版本:一个.tsv给Excel和脚本用,一个.md给GitHub、语雀这类平台渲染。好处是既能在编辑器里快速过滤,也能放进在线文档直接阅读。

Markdown格式其实很简单,每一行写成- [序号] 文件路径:摘要,再加一个二级标题,渲染出来就是漂亮的清单。如果后续想做成HTML,也可以直接在脚本里套个模板,把表格变成网页。我甚至试过把目录文档接入一个本地搜索页面,输入关键词就能跳转对应txt文件,但要额外处理路径转链接,工作量不大,比一个个翻文件舒服多了。

5.3 联动关键词和正则,让目录文档变成轻量检索引擎

如果你对内容有明确的关键词需求,比如只想找包含“龙族”或者“对话体”的txt,可以在提取前N个字符后加一个判断,用re.search把命中的文件单独输出一个分类目录。这样目录文档就具备了简单的检索能力。

更进一步,我还试过把所有txt开头和文件名拼成一个长字符串,用正则做多关键词匹配,然后输出一个命中清单。本质上和用文本编辑器搜索目录文档没有区别,但脚本化之后可以定时跑,适合每天新增文件的工作流。重命名文件也可以参考这个思路:先扫描目录,生成目录文档,再根据目录里的摘要修改文件名,做到内容和文件名统一。

说实话,这个脚本能跑通,最耗费心力的不是十几行代码,而是后面那几类边角料问题。我在给朋友跑数据时,一开始生成出的目录里有乱码、有自己、也有被换行拆散的行,修修改改才稳定下来。最后分享一个小建议:第一版先固定N值,比如100,输出成.tsv,跑通之后再加参数和过滤,避免一上来就把需求想复杂。

如果你手头也有一堆txt需要建立目录,不妨从这份代码抄起。它可以帮你从“翻开文件夹茫然四顾”变成“打开目录文档一目了然”。后续想加什么功能,欢迎留言聊,我这边还有几个正在玩的变体,比如按作者把小说生成多级目录、给每个txt自动生成一段摘要存成JSON,有空继续写。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询