DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

wc 统计文本比可见字符多,默认最后一列是字节数

GNU wc 默认输出字节数;统计字符应使用 -m 并核对文件编码和 locale,保留换行、CRLF 与编码错误的计数边界。

同一段文字在编辑器里数出几十个字符,wc file.txt 的最后一个数字却更大。文本从网页复制后差得更多,而把弯引号换成普通英文引号,数字又变了。这通常需要先核对统计单位:GNU wc 默认的三项是换行数、词数和字节数,最后一项不是字符数。

下面针对 Linux 上的 GNU wc,以及编码已知的普通文本文件。文件中有没有末尾换行、当前 locale 能否正确解释编码,也会影响计数;不能把所有差异都归因于多余空格。

用同一个文件比较字节与字符

wc -c -- ./message.txt
wc -m -- ./message.txt

./message.txt 是待检查文件的占位路径。-c 统计字节,-m 按当前 locale 统计字符。两个命令都只读文件,不需要先改内容。

以有效 UTF-8 文本为例,普通英文单引号 ' 占一个字节,弯引号 ’ 占三个字节。将后者替换成前者,字节数会减少二,但字符数不因此减少。中文等非 ASCII 字符也可能占多个字节,不能把字节总数当成文字长度。

末尾的换行同样属于输入。若文件在可见文字后还有一个 LF,字符计数会把它算进去。不要为了与肉眼计数一致,就无条件从每份结果减一:空文件、没有末尾换行的文件、多行文件都不符合这个假设。

确认统计进程使用了正确编码环境

locale
locale -a

先确认文件实际编码,再从本机可用列表中选择匹配的 locale。若已确认文件为 UTF-8,且系统提供 C.UTF-8,可以只对本次统计指定它:

LC_ALL=C.UTF-8 wc -m -- ./message.txt

这里的 C.UTF-8 是有前提的示例,不是所有系统都提供的固定名称。机器只提供其他 UTF-8 locale 时,使用其实际名称;设置一个不存在的名字不能获得可靠的解码结果。临时的命令前缀只影响这一进程,不需要为了统计文件改全机语言设置。

GNU wc 对编码错误的处理也不能忽略:-m 不把编码错误计作字符。因此,一个偏小的字符数不证明文本已经合法或完整。文件是其他编码、混合编码或损坏输入时,应先按实际来源核对编码,不能反复更换 locale 直到数字“看起来对”。

若终端里正常、定时任务里不同,比较的是两个任务各自继承的 locale,而不是只看登录终端。字节数不依赖把 UTF-8 字节正确解释成字符,字符计数则依赖这一条件。

对仍然不明的差异查看原始字节

od -An -tx1 -c -- ./message.txt

这会把输入显示为十六进制和字符表示。重点检查行尾的 LF、CRLF、不可见空白以及非 ASCII 标点。大文件先选取经过脱敏的小样本,不要把包含凭据的完整字节转储贴到公开渠道。

例如一个 CRLF 行尾含 CR 和 LF 两个字符;它与只含 LF 的文件,即使在编辑器里显示为同一行,也不具有相同的原始字符或字节序列。发现这种区别以后,要根据消费该文件的软件要求决定是否转换,不能为了凑计数直接删掉所有控制字符。

将正确单位接回校验规则

接口限制若按字节计费或限制传输大小,应继续用字节数;若要按当前编码统计字符,才采用 -m。GNU wc 的字符计数也不是界面显示宽度,不能拿它直接决定终端占几列;wc -L 统计的是另一种最长显示宽度。

修改校验脚本后,至少检查纯 ASCII、含多字节字符、带与不带末尾换行以及空文件。保存旧的统计规则和输入样本,确认下游没有把单位混用。仅调整只读统计命令无需恢复文件;如果还做了编码或换行转换,应保留原文件,不能指望改回 wc 参数就恢复已经改变的字节。