我有一个文本文件,其内容是:
i k k
当我用wc -m
它来计算此文件的字符数时,结果是7。
问题 1:但是我为什么得到 7,我不应该得到“6“假设它算上”行结束“ 特点?
问题2:具体是如何wc -m
工作的?
问题 3:当我使用wc -c
(计算字节数)时,我得到的结果与 相同wc -m
,因此有两个不同的选择有什么意义?它们的作用完全一样,不是吗?如果不是,它们有什么区别,又是如何wc -c
工作的?
答案1
你确实应该只有 6 个字符。尝试运行
cat -A filename
要查看文件中的非打印字符。你一定有额外的东西。如果我创建一个和你一样的文件,我会看到
i k k$
你加了空格吗?那将是 7:i k k $
或者可能有一个换行符:
i k k$
$
也是 7
正如你所说
wc -m
计算字符数并
wc -c
计数字节数。如果所有字符都是 ASCII 字符集的一部分,则每个字符只有 1 个字节,因此您将从两个命令中获得相同的计数。
尝试使用非 ASCII 字符的文件:
$ echo ك > testfile
$ wc -m testfile
2 testfile
$ wc -c testfile
3 testfile
啊哈!现在字节比字符还多。
答案2
$ locale charmap
UTF-8
在我当前的环境中,字符集是 UTF-8,也就是说,每个字符用 1 到 4 个字节进行编码(尽管由于 UTF-8 的原始定义允许字符代码点最多为 0x7fffffff,因此大多数工具可以识别最多 6 个字节的 UTF-8 字节序列)。
在该字符集中,Unicode 中的所有字符都可用,例如,aa
被编码为字节值 65,a乕
被编码为 3 个字节 228 185 149 以及é
被编码为两个字节序列 195 169。
$ printf 乕 | wc -mc
1 3
$ printf a | wc -mc
1 1
现在:
$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15
我修改了我的环境,其中的字符集现在是 ISO-8859-15(其他内容,如语言、货币符号、日期格式也已被修改,这些区域设置的集合被称为区域设置)。我需要在该环境中启动一个新的终端仿真器,以便使其字符渲染适应新的语言环境。
ISO-8859-15 是单字节字符集,这意味着它只有 256 个字符(实际上实际涵盖的字符甚至更少)。该特定字符集用于西欧语言,因为它涵盖了西欧大部分语言(以及欧元符号)。
它具有a
字节值为 65 的字符,类似于 UTF-8 或 ASCII,它还具有字符é
(例如法语或西班牙语中常用的),但字节值为 233,它没有 乕 字符。
在那种环境下,wc -c
总是wc -m
会产生相同的结果。
在 Ubuntu 中,与大多数现代类 Unix 系统一样,默认设置通常是 UTF-8,因为它是唯一支持覆盖整个 Unicode 范围的字符集(和编码)。
还存在其他多字节字符编码,但它们在 Ubuntu 上没有得到很好的支持,您必须经过一番周折才能使用这些编码生成语言环境,如果这样做,您会发现很多东西都无法正常工作。
因此在 Ubuntu 上,字符集要么是单字节,要么是 UTF-8。
现在,还有几点说明:
在 UTF-8 中,并非所有字节序列都构成有效字符。例如,所有非 ASCII 的 UTF-8 字符都由所有设置了第 8 位的字节构成,但只有第一个字节设置了第 7 位。
如果您有一个字节序列,其中的第 8 位被设置,而没有一个字节的第 7 位被设置,那么它就无法转换为字符。这时您就会开始遇到问题和不一致,因为软件不知道如何处理这些问题。例如:
$ printf '\200\200\200' | wc -mc
0 3
$ printf '\200\200\200' | grep -q . || echo no
no
wc
grep
在其中找不到任何字符,但是:
$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3
bash
找到 3. 当它无法将字节序列映射到字符时,它会将每个字节视为一个字符。
情况可能会变得更加复杂,因为 Unicode 中有些代码点作为字符无效,有些则非角色,并且根据工具的不同,它们的 UTF-8 编码可能会或可能不会被视为字符。
另一件需要考虑的事情是字符和字素之间的区别,以及它们的呈现方式。
$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
3 6
在那里,我们将 3 个字符编码为 6 个字节并呈现为一个字素,因为我们将 3 个字符组合在一起(一个基本字符、一个组合尖音符和一个组合封闭圆圈)。
wc
Ubuntu 上的GNU 实现有一个-L
开关可以告诉您输入中最宽行的显示宽度:
$ printf 'e\u301\u20dd\n' | wc -L
1
你还会发现有些角色占据了 2细胞在宽度计算中就像我们乕
上面的字符一样:
$ echo 乕 | wc -L
2
总结一下:通俗的讲,字节、字符、字素并不一定相同。
答案3
wc -c
和的区别在于wc -m
,在具有多字节字符的区域设置(例如 UTF8)中,前者计算字节数,而后者计算字符数。考虑以下文件:
$ hexdump -C dummy.txt
00000000 78 79 cf 80 0a |xy...|
(对于那些不懂 UTF8 的人来说,这是字母 'x'、'y' 和 'π',后跟换行符)。它有五个字节长:
$ wc -c dummy.txt
5 dummy.txt
但只有四个字符长:
$ wc -m dummy.txt
4 dummy.txt