为何LANG=en_US.UTF-8时GNU comm命令无法区分✓与⨯?
为何UTF-8环境下GNU comm无法区分两个文件的内容?
问题背景
我有两个文件:
- a.txt内容:
meow ✓ bar
- b.txt内容:
meow ⨯ bar
执行不同LANG设置下的comm命令,结果差异明显:
LANG=C时的输出
$ LANG=C comm a.txt b.txt meow ✓ bar meow ⨯ bar
LANG=en_US时的输出
$ LANG=en_US comm a.txt b.txt meow ✓ bar meow ⨯ bar
LANG=en_US.UTF-8时的输出
$ LANG=en_US.UTF-8 comm a.txt b.txt meow ⨯ bar
补充的文件字节码输出(od -c):
$ od -c a.txt 0000000 m e o w 342 234 223 b a r \n 0000015 $ od -c b.txt 0000000 m e o w 342 250 257 b a r \n 0000015
原因分析
comm命令的核心行为完全依赖当前系统的locale排序规则,且要求输入文件必须提前按对应规则完成排序,否则输出结果无意义。
C locale(或非UTF-8 locale)的情况
当LANG=C时,系统采用严格字节序排序——直接比较字符的ASCII/字节值。a.txt中的✓(UTF-8字节为0xE2 0x9C 0x83)和b.txt中的⨯(UTF-8字节为0xE2 0xA8 0xAF)字节序列完全不同,因此两行被判定为不同内容。同时因为这两行的字节序本身是a在前、b在后,符合C locale的排序顺序,所以comm能正确区分。UTF-8 locale的情况
当LANG=en_US.UTF-8时,系统使用针对UTF-8设计的排序规则,这类规则会对非字母数字的符号做特殊处理:要么忽略符号的差异,要么给部分符号赋予相同的排序权重。这里的✓和⨯都属于符号类,在排序时被视为等价内容,导致comm认为两行完全相同。
另外,你没有提前用LANG=en_US.UTF-8 sort对文件排序,这进一步放大了结果的异常——comm的设计逻辑就是基于“输入已按当前locale排序”的前提,未排序的输入会导致错误的匹配判定。
验证方法
如果按对应locale先排序文件再执行comm,就能看到明确的差异:
- UTF-8 locale下排序后执行comm:
LANG=en_US.UTF-8 sort a.txt > a_sorted.txt LANG=en_US.UTF-8 sort b.txt > b_sorted.txt LANG=en_US.UTF-8 comm a_sorted.txt b_sorted.txt
此时两行会被判定为共同行,因为排序规则将它们视为等价。
- C locale下排序后执行comm:
LANG=C sort a.txt > a_sorted.txt LANG=C sort b.txt > b_sorted.txt LANG=C comm a_sorted.txt b_sorted.txt
结果会和直接执行LANG=C comm一致,两行被明确区分。
内容的提问来源于stack exchange,提问作者binaryfunt
相关产品推荐
相关产品推荐

