Linux 中的“wc -c”和“wc -m”命令

Linux 中的“wc -c”和“wc -m”命令

我有一个文本文件,其内容是:

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

wcgrep在其中找不到任何字符,但是:

$ 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 个字符组合在一起(一个基本字符、一个组合尖音符和一个组合封闭圆圈)。

wcUbuntu 上的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

相关内容