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


24

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

i k k

当我wc -m用来计算此文件上的字符数时,结果为7

问题1:但是为什么我得到7,为什么我不应该假定它计算“ 行尾 ”字符而得到“ 6 ” 呢?

问题2:wc -m工作原理如何?

问题3:使用wc -c(计算字节数)时,我得到的结果与相同wc -m,那么拥有两个不同选项的意义何在?他们做的完全一样,不是吗?如果没有,有什么区别,如何wc -c运作?



1
如果您的文件来自带有CRLF行尾的Windows,那么您也可能会得到7
Chris H

Answers:


36

实际上,那里确实应该只有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

啊哈!现在,字节多于字符。


3
我使用“ cat -A ”命令,最后发现在“ 行尾 ”字符($)之前有一个空格。这就是为什么我得到7而不是6的原因。谢谢,“ cat -A ”帮助很大。
SWIIWII

2
@SWIIWII是的,我刚刚在我的回答中添加了该内容,因为我认为可能是这样的:)
Zanna

1
换行符也算在内。即使它是不可见的,它仍然是一个字符,在文件中算作数据块。很好地使用cat -A。曾经也可以使用hexdump或xxd来做同样的事情
Sergiy Kolodyazhnyy

@Serg是的,cat -A也会显示出来。我添加了我的答案,谢谢:)
Zanna

@SWIIWII将代码放在反引号中`likethis`以使其可读,而不要使其粗体
phuclv

2
$ locale charmap
UTF-8

在我当前的环境中,字符集为UTF-8,即每个字符用1到4个字节进行编码(尽管由于UTF-8的原始定义允许字符代码指向0x7fffffff,所以大多数工具会识别UTF- 8个字节的序列,最多6个字节)。

在该字符集中,所有来自Unicode的字符都是可用的,例如a a被编码为字节值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(语言,货币符号,日期格式等其他内容也已修改,这些区域设置的集合称为locale)。我需要在该环境中启动一个新的终端模拟器,以使其字符渲染适应新的语言环境。

ISO-8859-15是单字节字符集,这意味着它只有256个字符(实际上比实际覆盖的字符还要少)。该特定字符集用于西欧语言,因为它涵盖了其大部分语言(和欧元符号)。

它具有a字符值(如UTF-8或ASCII中的字节值65),也具有é字符(例如,在法语或西班牙语中通常使用的字符),但具有字节值233,则不具有乕字符。

在那种环境下,wc -c并且wc -m将始终给出相同的结果。

在像大多数现代Unix系统一样的Ubuntu中,默认值通常是UTF-8,因为它是覆盖整个Unicode范围的唯一受支持的字符集(和编码)。

还存在其他多字节字符编码,但是在Ubuntu上并没有很好地支持它们,因此您必须经过箍才能使用它们生成语言环境,如果这样做,您会发现很多事情不好好工作。

因此,对于Ubuntu而言,字符集要么是单字节,要么是UTF-8。

现在,更多注意事项:

在UTF-8中,并非所有字节序列都形成有效字符。例如,所有不是ASCII字符的UTF-8字符都是由字节组成的,这些字节都设置了第8位,但是只有第一个设置了第7位。

如果您将字节序列的第8位设置为1,而没有一个字节序列将第7设置为1,则无法将其转换为字符。那就是当您开始遇到问题和不一致时,因为软件不知道该如何处理。例如:

$ 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个字节,作为一个graphem进行渲染,因为我们将3个字符组合在一起(一个基本字符,一个组合的重音符号和一个组合的围圈)。

wc在Ubuntu上找到的GNU实现有一个-L开关来告诉您输入中最宽行的显示宽度:

$ printf 'e\u301\u20dd\n' | wc -L
1

您还会发现一些字符在宽度计算中占据了2个像,就像我们上面的字符一样:

$ echo 乕 | wc -L
2

结论:在荒野的单词中,字节,字符和字素不一定相同。


1

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

或者,甚至考虑UTF-32,其中每个字符都有4个字节。
约尔格W¯¯米塔格
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.