
有人说整理文件这种事最烦的不是文件多而是“看起来全是一堆文档实际不知道谁有内容、谁是空壳”。我自己就栽过很多次跟头想从几千个 txt 和 pdf 里把手头的素材归个类按大小筛不对按名字猜更不靠谱。真正能帮我做决定的反而是文本字数——也就是提取出每个文件的正文内容统计出有效字数是 100 还是 10 万。按文本字数筛选文件这个需求听起来懂的人很少但只要你试过一次批量处理就会再也回不去纯按文件大小排序的日子。这篇教程要聊的这个工具本质上就是把“拆文档、提文本、数字数、按区间过滤”这四步全自动化的一个命令行小工具它支持 TXT、DOCX、PDF、PPTX、HTML、MD 这几种最常见的格式也预留了扩展 epub 或纯文本日志的空间。你只需要给它一个目录它能递归扫描、把字体编码和标签里的干扰项处理掉、统计出相对准确的字数然后输出一张 CSV 或 JSON 报告也可以直接带--min/--max参数筛选出符合要求的文件。无论你是要清理资料库、整理电子书还是要做语料预筛或者排查看起来很大的“假大空” PDF这套操作方法都能直接套用。1. 为什么你需要的是“字数”而不是“文件大小”1.1 三种让我不得不用它的真实场景第一种场景是挑语料。我曾经从网上扒过一批网页存档也整理过一批从不同渠道收集回来的小说 txt、公众号文章 html 和项目说明 md总量大概有三千多个文件。下一步要做分类或者去重但我在乎的是“这篇文章到底写了多少内容”。文件名经常是新建文档(213).txt这种鬼样子不打开根本不知道内部是什么靠大小更不科学同样是一万字纯文本可能有 30KB带格式的 docx 却可能因为嵌入图片而变成 500KB。这时候只有统计字数最直接。第二种场景是找“看起来很大实际没内容”的文件。这个坑在 PDF 上尤其常见。我自己就翻出来过一个 8MB 的 PDF打开以后只有一张扫描图片整份文档没有一个可复制的文字。如果你照文件大小管理这种文件会一直占地方但用字数工具一扫它显示的文本量只有 12 个字符立刻原形毕露。网页另存为的 html 也一样有的页面打开满屏都是导航栏和广告代码正文连二百字都没有按字数筛出来以后我会直接归档而不是让它混在资料里。第三种场景是快速检索“过短或过长”的文件。比如我想找某个目录里字数在 500 到 2000 之间的短文案或者找字数超过 10 万的长篇小说这本质上就是一个带条件的全目录统计。以前我只能写一堆 find 命令再配合 wc现在这个工具一条命令就能把结果列出来。1.2 “按字节”和“按字数”是两套完全不同的思路文件系统给你的“大小”维度只能反映磁盘占用反应不了人类真正关心的信息量。一个 20KB 的空文档和一个 20KB 的纯文字文档字号一样内容量天差地别。所以我把“按字节筛选”和“按字数筛选”分开看待前者是存储视角后者是内容视角。做真正的文件清洗和数据整理时你需要的往往是内容视角。以一万字的纯中文 txt 为例按 UTF-8 编码大概 30KB但一份 30MB 的 PDF 可能只有几十个字因为它内部嵌了高分辨率扫描图。如果你用体积衡量会觉得 PDF 更“充实”可对我们的用途来说它就是虚胖。这个工具统计出的字数并不会代替文件大小它更像是给每个文件补了一个“内容密度”标签让两个维度交叉着看很多问题就清楚了。2. 不同格式的提取原理与隐藏的坑2.1 TXT 和 MD读起来最简单但编码与标记是暗坑纯文本文件是基础中的基础工具内部直接按二进制读取再做编码识别。你以为简单实际上一上来就遇到两个问题第一是编码猜不对。国内大量老 txt 是 GBK 或 GB18030网上另存的文件又可能是 UTF-8 带 BOMUTF-16 也有。这个工具默认会尝试 UTF-8、GB18030、UTF-16 的顺序实在识别不了就用 chardet 猜测你也可以用--encoding参数强制指定。第二是 MD 文件不能直接数。Markdown 里的#、**、链接语法[]()和图片语法![]()如果全数进去字数就会被污染。工具的处理方式是先识别代码块比如一段 python 代码放在里默认不计入正文字数然后把标题符号、列表符号、引用符号、链接和图片语法去掉只保留可阅读文本。这个设计对写技术笔记的人特别友好因为我筛选 md 文件时只关心“正文干货有多少”而不是把配置代码也当内容。2.2 DOCX 与 PPTX本质是 zip 压缩包必须解包读 XML我最初也犯过低级错误直接拿文本方式去读一个.docx结果满屏乱码。后来才意识到docx 虽然是“文档”却是 zip 格式的容器。真正的正文在word/document.xml这个 XML 文件里被一层又一层的标签包住。工具的做法是先解压这个 zip再遍历 XML 里的文本节点w:t把段落合并起来同时把页眉页脚、批注、文本框这些干扰项按参数决定是否计入。PPTX 同理它是另一个 zip 包正文散落在ppt/slides/slideN.xml里。这里有一个需要你自己拿主意的点PPT 的备注文字算不算正文我的默认是不算因为过滤文件时通常只关注幻灯片上展示出来的内容但如果你整理的是演讲稿素材可能希望把备注也算进去工具提供了一个开关看你实际需求决定。2.3 PDF最复杂也最容易统计出“假零”PDF 是这个工具里最费劲的部分原因是它既有文本对象也有图片对象还可能根本没有文本层。扫描版的 PDF 本质上是一堆图片提取不到任何字符统计结果就是 0。而正常电子版 PDF 里文字以文本对象形式存在但分栏、表格、页眉页脚都会干扰统计。工具内部用的是 pdfplumber 这类解析库会尽量按文本块顺序提取但遇到复杂版式时字数依然可能有轻微误差。这里我必须说明一个统计口径问题一个 PDF 如果只有一张图片那它值得有 0 字吗我个人认为从“内容筛选”的角度看它就是没有文字内容但如果你需要知道“有没有图”应该去看文件大小和页面尺寸。所以这个工具的输出报告里会同时给文件体积让你自行判断“0 字的 PDF 是不是图片型文档”。2.4 HTML标签不算内容脚本和样式更不算网页另存为或爬虫抓下来的 HTML 文件包含了大量标签、脚本、样式和注释。如果不处理一个 200KB 的 HTML 文件统计出来可能有几万“字”但真正的正文可能只有一千字。工具的提取思路是用解析器把script、style、noscript注释和元信息全部剔掉再对剩余的正文标签去标签化同时把nbsp;、amp;这类实体符号还原成正常字符。不过 HTML 里还有一个容易忽略的干扰项隐藏文字和属性文本。比如页面里有个title标签里面的内容属于浏览器标签页上显示的东西严格来说不算正文。我的做法是默认只统计可见区文本避免把 meta 描述、关键词这种 SEO 填充物算进去。你要是只想粗略了解页面大小可以选“保留隐藏文本”模式但日常筛选我建议关闭。3. 完整使用流程从安装到最终报告3.1 准备运行环境这是一个 Python 3 脚本依赖pdfplumber、python-docx、python-pptx、beautifulsoup4、lxml这几个库。安装命令不复杂pip install pdfplumber python-docx python-pptx beautifulsoup4 lxml装好后把text_count_filter.py放到任意目录命令行里cd进去或者把它加入 PATH 方便全局调用。我平时把脚本放在~/tools/下面然后在 shell 里配置一个别名这样整理文件时不用每次先找路径。3.2 命令行参数速查进入正题工具的使用方式如下python text_count_filter.py --dir D:/docs --types txt,md,docx,pdf,pptx,html --min 100 --max 50000 -o result.csv参数含义我用表列出来方便你复制后直接调整参数作用说明--dir必填要扫描的目录支持绝对路径和相对路径--types扩展名过滤默认是全部支持格式用逗号分隔--min最小字数只保留字数大于等于该值的文件--max最大字数只保留字数小于等于该值的文件--encoding手动指定文本编码适合你知道文件固定编码的情况--no-recursive不递归子目录只扫一层--include-hidden包含隐藏目录默认跳过.git、__MACOSX等--count-by统计口径char按字符数word按单词数line按行数--output输出报告文件支持 csv 或 json这一套参数设计得比较直白。你甚至可以一个参数都不用直接跑命令扫描当前目录它会把所有支持格式的文件字数都列出来再慢慢看。3.3 一次真实运行的完整记录我给你看一次我实际跑过的例子。当时我在整理一个叫资料/待办的目录里面有三百多个文件我想把所有 100 字以上、5 万字以下的 docx、pdf、txt 筛出来同时输出一份 CSVpython text_count_filter.py --dir /Users/me/资料/待办 --types txt,md,docx,pdf --min 100 --max 50000 -o result.csv终端会先显示一个扫描进度条然后输出类似这样的摘要扫描目录: /Users/me/资料/待办 文件总数: 318 匹配区间文件: 196 被过滤文件: 122 耗时: 6.82 秒 报告已生成: result.csv打开result.csv你会看到类似表格的行文件路径类型字数文件大小最后修改时间资料/待办/第一章.txttxt2850082.1KB2024-03-12资料/待办/合同扫描.pdfpdf05.2MB2024-01-05资料/待办/会议纪要.docxdocx86018.3KB2024-11-20看到没有那个 5.2MB 的 PDF 字数却是 0它明显是扫描件下一步要处理它就得靠 OCR 或者肉眼确认而不是继续让它混在正常文档里。3.4 输出报告后可以做什么CSV 报告能直接用 Excel 或 WPS 打开也能用 Python 的 pandas 做二次分析。我常用的一招是把体积和字数两个字段同时取出来生成一个“内容密度”指标比如大小(字节) / 字数。密度特别高的文件基本都是嵌了很多图片或格式的文档密度低到离谱的大概率是空文档。如果你写了一个自动化脚本也可以指定--output result.json这样就能把文件列表直接交给下一步流程。比如我要预处理一批语料会先用工具筛出字数在 3000 到 50000 之间的 txt 和 md再把 JSON 里的文件路径交给下一个脚本做合并。输出的文件路径是绝对路径省得我再拼一次路径字符串。4. 各格式的实测对比与取舍建议4.1 一张速查表我把自己实际测试各格式的结果整理成了一张表它基本决定了你对统计结果的信任程度应该有几分格式提取策略统计可信度容易踩的坑txt纯文本直接读高编码识别、BOMmd去语法后读高代码块和链接被误算docx解 zip 读 document.xml高页眉页脚与批注混入pdf解析文本对象中扫描件没有文本层pptx解 zip 读 slide XML高备注是否算正文html去标签和脚本中隐藏文字、SEO 填充信任度最高的当然是 txt 和 md因为它们没有复杂结构。docx 和 pptx 只要解析逻辑正确可信度也很高但你必须自己决定统计口径页眉页脚、备注和批注到底算不算正文。这个工具默认会把页眉页脚和批注会排除但在参数里可以打开你按自己场景灵活选择。4.2 几个特殊场景下我推荐的开关组合如果你处理的是小说网站爬下来的 txt建议加上--encoding utf-8强制指定或让工具自动检测避免 GBK 文件被误读成乱码之后字数统计彻底走样。如果你处理的是从前端保存下来的整站归档 html建议用默认设置也就是剔除script和style但如果你只是想粗略比较页面大小用--count-by char统计所有字符数也行这时候结果会偏大你的筛选阈值也要相应提高。如果你在检查 PDF 资料库不要只依赖字数一定要把文件大小也拉出来一起看。一个 0 字但 5MB 的 PDF 基本可以判为图片型一个 500 字但只有 200KB 的 PDF说明它可能只是某篇文章的短摘要价值不高。这两个维度交叉使用比单独看任何一个都靠谱。5. 常见问题与排查技巧实录5.1 PDF 提取结果显示 0是怎么回事这是最高频的问题。根据我的经验八成以上是因为扫描版 PDF。它没有文本层所有内容都是图片提取工具不可能凭空给你变出文字。少数情况是 PDF 做了权限限制或字体编码特殊也会导致提取不到完整文本。排查方式很简单用 PDF 阅读器打开试着用鼠标选中页面上的文字。能选中的就是带文本层完全选中不了基本就是扫描图。如果你确实需要统计扫描版的字数那就要走 OCR 流程。我的工具默认不做 OCR因为它会明显拖慢速度而且中英文 OCR 还需要本地模型配合。更实际的办法是先把这个文件筛出来集中交给专门的 OCR 软件处理再把 OCR 出来的 txt 重新放进目录里统计。5.2 为什么和 Word 状态栏里的字数对不上Word 左下角状态栏默认显示的是“字数”它有自己的统计规则中文按字数、英文按 word 可能会算成词组而我们工具默认的--count-by char是把所有可见字符都数一遍空格、换行、制表符不算。这两种口径不一样数字对不上是正常的。举例来说一篇英文文档里写了“machine learning is fun”。Word 可能按空格分词算 4 个词工具按 char 统计则算 18 个字符。如果你更关心阅读量建议直接按char如果更关心“几个词”改用--count-by word就好。不要在不同口径下对比数据那是自己给自己找麻烦。5.3 HTML 文件统计出来有一大堆“字”感觉不对遇到这种情况先怀疑标签没处理干净。部分网页代码里把大量关键词塞在meta、注释或者隐藏的 div 里如果工具没有过滤脚本、样式、隐藏元素这些内容都会混进字数。我的做法是默认剔除这些区域但如果你用的自动化方案不同很可能踩这个坑。另外HTML 的编码声明也很重要。有的网页声明是 UTF-8实际用 gzip 压缩传输另存本地后文件内容却是乱码工具此时可能识别不出正确编码。我建议遇到具体文件时用--encoding强制指定或者先手动打开看一眼内容再做判断。5.4 中英文混排时的字数统计要不要分开看严格来说“字数”这个概念在中英文里的含义不太一样。中文每个汉字都有独立意义一个字就是一个基本单元英文则是以空格分隔的单词为基础。工具默认把中文汉字、英文单词里的每个字母都当作字符这样得到的是一个偏“文件内容长度”的数字。如果你的目标是快速对比文件大小档位这个数字完全够用但如果你要把统计结果做成研究性数据建议自己定义好口径并统一用--count-by word或者统一用--count-by char不要混用。我自己遇到过最无语的情况是同一批文件第一次跑用 char第二次跑用了 word结果两份报告的排序完全不一样害得我花了半天排查。后来我养成了一个习惯每个统计任务开始前先确认口径并且把口径写进输出文件名里例如result_char.csv避免以后忘掉。5.5 目录特别大扫描太慢或者卡住怎么办如果目录里有几万个文件包括一堆巨大的 PDF扫描速度确实会受影响。我的工具默认是一个文件处理完再处理下一个内存占用其实不大但 CPU 会跑满。碰到这种情况我建议分片处理先用--types txt,md扫文本类速度极快再单独用--types pdf扫 PDF体会一下真实耗时大目录下优先用--no-recursive只扫第一层或者按子目录分批跑。还有一个容易被忽略的点如果目录里有一个几十 MB 的 PDF它的文本提取耗时会比一百个小 txt 都长。如果你想快速看结果可以先用--max限制文件大小吗目前不支持但你可以提前把超大的 PDF 移到另一个目录或者先用find命令挑出来。总的来说几千个普通文档在十秒内跑完不算难事真遇到十几万个文件的仓库还是老老实实分片。6. 三个能直接抄作业的组合用法6.1 清理低质量 txt 和 md我有很多从不同渠道收集来的文本素材里面短到只有几十个字的碎片不少。我给自己定的清理规则是只保留 500 字以上、10 万字以下的文件。命令这样写python text_count_filter.py --dir D:/素材库 --types txt,md --min 500 --max 100000 -o 素材清选.csv跑完之后低于 500 字的碎片会全部被过滤掉我再用这个 CSV 反向生成一个待删除清单。这里提醒一句不要直接拿着结果删除文件先人工抽查几个确认你想要的都在范围内再执行删除或归档。6.2 揪出“虚胖” PDF如果你怀疑某个目录里有扫描件冒充电子书可以用这个组合先扫描全部 PDF再按“体积大但字数少”来排序判断。我一般连 CSV 都不导直接在终端加个--min 0 --max 50把它当成“低文本量文件筛选器”。跑出来的结果配合文件大小看一眼就能发现问题文件。python text_count_filter.py --dir D:/电子书 --types pdf --min 5 --max 50 -o 疑似扫描件.csv因为一个真正的电子书 PDF 再怎么短也有几百字低于 50 字的几乎可以断定是扫描页或封面页。这套逻辑同样适用于图片型 PPTX。6.3 按主题目录快速摸底有时候我接手一个陌生团队的共享文件夹还没来得及看文档内容先想知道哪些目录是“有干货的”。我会对顶层每个子目录分别执行一次不带--min/--max的扫描然后看整体字数分布。哪个目录里 1 万字以上的文件占比高通常就是核心资料目录哪个目录全是几百字的碎片可能只是临时存档。这时候统计结果比文件大小分布靠谱得多因为你会发现有些子目录虽然大但都是浏览器缓存和图片真实文档少得可怜。这个“内容密度”的视角对快速了解自己面对的是一堆什么数据非常有用。7. 统计口径与使用习惯上的几点心得有几个心得是踩过坑之后才总结出来的。首先不要迷信单一指标。字数筛选工具适合做“初筛”它能帮你快速划出范围但不代表字数多就是好文件。我会在跑完字数之后把结果和文件大小、修改时间、文件来源综合起来看再决定下一步动作。其次每次批量操作都先导出一份完整的 CSV 报告留底。我吃过不少亏比如跑完命令后没有留报告结果后续想复盘却没有数据。现在我养成了习惯只要执行筛选就带上-o参数把结果保存下来。哪怕你当时用不到这份报告以后也能当审计足迹。最后关于格式扩展。工具目前支持这六种常见格式但它的解析逻辑并不难延伸到.epub、.rtf、.log等文本型文件。我自己在用的版本已经加了 epub 的支持因为 epub 本质上也是 zip正文是 XHTML完全可以照搬 html 的清洗思路。如果你经常和特定格式打交道建议读一读源码改改对应的解析函数就能扩展出你要的类型。说到底按文本字数筛选这个需求并不起眼但真正做起来会牵扯到编码、文件格式、压缩结构、统计口径这些杂七杂八的问题。我给出的这些原理、参数和排查方式都是实际跑过的经验你拿到之后先找一个几十文件的目录试一遍输出报告对比一下再决定要不要正式用来管整个资料库。