Linux文件字数统计
背景是这样的。最近我在升级维护自己的日记本系统,想要给目录页增加字数统计的功能。
这个系统的工作原理是这样的:先遍历当前文件夹下的所有txt文件,然后根据文件名(就是日记的创建日期)进行排序,并按月份分类,最后根据一个HTML模板生成最终的目录页。这个日记本系统是用Python编写的,但最初为了方便,我在代码中大量使用了Linux的shell指令(例如 ls 获取文件名),因此这次升级时我顺便探索了Linux下统计文件字数的方法。
字数统计初印象: wc指令的用法
学过Linux的人或多或少听说过wc指令,其全称是 word count ,可以用来统计字数和行数。下面是Linux manual page中对这个指令的介绍:
1 | NAME |
例如,我们在生信分析pipeline中常常会用到 wc -l <file> 统计文件的行数,也可以使用 wc -c <file> 或者 wc -w <file> 统计文件的bytes数或单词数。但这些并不是我们想要的——日记文件多以utf-8编码的中文字符写成(也会混有数字和英文字母),而 wc -c 统计的是总bytes数,它会把一个中文字符算作3个bytes,导致统计偏差。
那么,该怎么有效统计字符数呢?
-m 参数的妙用,以及LOCALE设置对统计结果的影响
在 wc 指令的manual page中提到了 -m 指令,其作用是统计字符数量,对我们来说也许有用:
1 | -m, --chars |
比方说,我们统计一下北宋词人柳永的《蝶恋花》:
1 | ~/sd/termux $ cat test.txt |
全词算上标题和标点符号共92个字符,但是使用 wc 直接统计,会统计出来265个bytes的大小。相比之下, wc -m 的统计结果是对的。
到此为止,我们本以为故事已经到达了终点,只需要将 wc -m 写入Python文件的字数统计模块,一切就大功告成了
——直到第二天我看到了服务器自动更新以后的字数统计结果——依然不对。
多说一句,这个日记本系统每天会自动扫描并更新一次目录,这个自动扫描的功能托管在Linux的cron服务下。
询问了AI,它告诉我:“crontab环境中,可能缺少正确的locale设置,导致wc命令默认使用C locale,从而将中文字符按字节统计。 解决方法有两种:1. 在crontab中设置正确的locale环境变量。 2. 在Python中使用其他不依赖locale的方法来统计字符数”。为了方便调试,这里我自然选择了方案1。
1 | (base) cyclin@vm:~$ LANG=C.UTF-8 wc -m test.txt |
如上所示,当我们在调用 wc 指令前强制设置环境变量 LANG ,那么 LANG 的取值会影响 wc -m 的统计结果。在termux环境下测试时大部分LANG设置都能正常统计字数,但是在服务器端(Ubuntu 22.04),只有当设置 LANG=C.UTF-8 时才能让 wc -m 正确识别中文字符。
总结:要想正确统计字符数(并且不依赖于环境变量),那么应该使用这个指令: LANG=C.UTF-8 wc -m <file>
后记:在上述探索之后,DeepSeek建议我如果想要提高性能和安全性,就不要再继续用Linux shell指令进行文件名读取和字数统计了,使用Python内置的文件接口 os.listdir 和 file.read 是更安全和高效的选择。因此,我又花了一些时间重构了代码——效果还是很显著的,每次扫描的时间从5秒多提升到了不到3秒。
AI可真好用呀。
以及,统计完以后我才发现,这几年下来我的日记居然攒到了一百多万字——原来我在日记里话可真多啊,笑死。