在过去的几个月里,我一直在 William E. Shotts 的帮助下学习命令行Linux 命令行。 Linux 命令行对于想要了解更多 Linux 命令行的新手来说,这本书仍然是一本受欢迎的书。
其中一章介绍了该tr
命令。书中说,字符集可以用以下三种方式之一构建:枚举列表,例如ABCDEFGHIJKLMNOPQRSTUVWXYZ
;字符范围,例如A-Z
;以及 POSIX 字符类,例如[:upper:]
。
我不明白的部分是,书中告诉读者要谨慎使用字符集的字符范围,因为区域设置的排序顺序,并建议读者改用 POSIX 字符类。
我个人从未遇到过使用字符范围的问题,A-Z
例如
echo "lowercase letters" | tr a-z A-Z
那么为什么我应避免使用字符范围而使用 POSIX 字符类呢?
如果你想知道,我的语言环境是 en_US.UTF-8。
答案1
您使用的是 UTF-8。耶!ASCII 以及 UTF-8(因为 UTF 人员试图使其成为 ASCII 的超集)的字母按字母顺序排列,没有间隙,因此a-z
包含所有常规小写字符,而不包含其他任何内容,依此类推。
然而,在其他编码上,情况不一定如此。经典的例子是EBCDIC:
字母之间的间隙使得在 ASCII 中有效的简单代码在 EBCDIC 中失效。例如,在 ASCII 字母表中
for (c='A';c<='Z';++c)
会设置c
为 26 个字母,但在 EBCDIC 中会设置为 40 个字符(包括许多未分配的字符)。解决这个问题需要使用函数调用使代码复杂化,这遭到了程序员的强烈抵制。
我想没有人再使用这种奇怪的东西了,但是谁知道呢?
据我所知,GNU tr 不支持 Unicode,但对于支持的程序,[[:upper:]]
也会匹配被视为大写字母的 Unicode 字符,例如全角“A”或带重音符号的 A:À。
$ printf "%s\n" A a A À | grep '[[:upper:]]'
A
A
À
$ printf "%s\n" A a A À | grep '[A-Z]' # I'm also using Unicode, so grep tries to be friendly
A
À
$ printf "%s\n" A a A À | LC_ALL=C grep '[A-Z]'
A