LC_COLLATE是否(应该)影响字符范围?


27

整理顺序LC_COLLATE不仅定义了各个字符的排序顺序,而且还定义了字符范围的含义。还是呢?考虑以下代码段:

unset LANGUAGE LC_ALL
echo B | LC_COLLATE=en_US grep '[a-z]'

直观上,Bnot in中[a-z],因此不应输出任何内容。这就是在Ubuntu 8.04或10.04上发生的情况。但是,在某些运行Debian lenny或squeeze的计算机上,B发现了该字符,因为该范围a-z包括排序规则之间az排序规则中的所有内容,包括大写字母BZ

所有测试的系统的确en_US生成了语言环境。我还尝试过更改语言环境:在B上面匹配的机器上,{en_{AU,CA,GB,IE,US},fr_FR,it_IT,es_ES,de_DE}{iso8859-1,iso8859-15,utf-8}除日语(使用任何可用编码)和C/ 之外,每个可用语言环境(大多是基于拉丁语的:,也包括中文语言环境)中都会发生相同的情况POSIX

当您超出ASCII时,字符范围在正则表达式中意味着什么?为什么一方面某些Debian安装与另一方面的其他Debian安装与Ubuntu之间有区别?其他系统如何表现?谁是正确的,谁应该报告错误?

(请注意,我是专门询问字符范围的行为,例如[a-z]en_US语言环境中,主要是在基于GNU libc的系统上。我不是在问如何匹配小写字母或ASCII小写字母。)


两个Debian的机器,一个地方B是在[a-z]和一个地方是不是,输出LC_COLLATE=en_US locale -k LC_COLLATE就是

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=1
collate-codeset="ISO-8859-1"

和的输出LC_COLLATE=en_US.utf8 locale -k LC_COLLATE

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=2039
collate-codeset="UTF-8"

1
不会在我方便的Debian Lenny实例上重现。不过没有检查是否en_US生成。
alex

1
@alex如果未生成C语言环境,则将语言环境用作后备,并且其整理顺序为直字节值,因此B不会被匹配。在出现在的输出中的语言环境中进行测试locale -a
吉尔(Gilles)'所以

1
请注意,en_US与en_US.utf8不同,并且通常表示en_US.iso-8859-1,具体取决于安装的内容。如果en_US(不带后缀)未出现在语言环境-a的输出中,则实际上您没有该语言环境。LC_COLLATE = zh_CN语言环境-k LC_COLLATE显示什么?
尼尔·梅休

1
此后在这里出现了一个实际而非理论的问题:为什么awk正则表达式中的一系列小写字母都包含大写字母?
卡莱布(Caleb)

1
@isaac不幸的是,七年后,我似乎无法使用任何有问题的系统。它们都已升级或退役。
吉尔(Gilles)'所以

Answers:


3

如果您使用的不是C语言环境,则不应使用诸如范围之类的范围,[a-z]因为它们是与语言环境相关的,并且不一定总能提供您期望的结果。与您已经遇到的案例问题一样,某些语言环境将带变音符号(例如á)的字符与基本字符(即a)相同。

而是使用命名字符类:

echo B | grep '[[:lower:]]'

这将始终为区域设置提供正确的结果。但是,您需要选择语言环境以反映输入文本和您要应用的测试的含义。

例如,如果需要查找特定的字节值,请使用C始终可用的语言环境:

echo B | LANG=C grep '[a-z]'

如果这不能按预期工作,那确实是一个错误。


我知道,这不是我要的。我特别在问一个明确的范围是什么意思,以及为什么不同的发行版(即使使用GNU libc和GNU grep)具有不同的行为。(不赞成投票,因为即使您说的是正确的,也没有关系。)
吉尔斯

1
我的观点是,显式范围的含义取决于语言环境,并且不需要不同的系统以相同的方式定义其语言环境,因此这不是一个错误。从技术上讲,您正在滥用系统,因此您对获得“未定义”的行为不会感到惊讶。另外,一些人评论说他们无法在其Debian系统上重现该行为,因此您的系统似乎有些不寻常。
尼尔·梅休

1
我知道范围的行为取决于语言环境。我在问,使用Glibc的不同系统(事实证明,即使是同一Debian版本的不同安装)也有不同的行为。我已经将输出添加locale -k到我的问题中;这在两台Debian机器上是相同的,一台B在范围内,另一台不在范围内。顺便说一句,我不是在任何一台机器上都是root用户(所以作为管理员,这并不是什么奇怪的事情)。
吉尔(Gilles)“所以

echo "Baü" | LC_COLLATE=C grep -o '[[:lower:]]'返回aAND,üecho "Baü" | LC_COLLATE=C grep -o '[a-z]'仅返回a。在我眼里,“降低”是不是真的OP想要的东西
丹尼尔·阿尔德

不过,我的原始观点仍然成立:除非您在C语言环境中,否则请勿使用范围。我认为这与希望报告错误的OP有关。如果您不在C语言环境中,则使用范围的结果是高度不可预测的,因此永远不会被视为错误。另一方面,如果您需要查找特定的字节值,则只需使用C语言环境。我的第二点是,如果您确实要在语言环境中搜索小写字母,请使用字符类。即使OP可能没有一直在寻找这个问题,但其他人可能会发现这个问题。
尼尔·梅休

1

正则表达式中的范围应遵守排序规则设置。这是相关的标准:http : //pubs.opengroup.org/onlinepubs/007908799/xbd/re.html(查找“范围表达式”)。因此,echo B | LC_COLLATE=en_US grep '[a-z]'应在B给定相应区域的明智定义的情况下输出。我无法解释为什么有时这对您不起作用,但是如果我在正确安装和配置的非古代系统上遇到此问题,我会感到非常惊讶。


1
echo B | LC_COLLATE=en_US.utf8 grep '[a-z]' 在带有grep 2.10的Ubuntu 12.04上不打印任何内容。在带有grep 2.6.3的Centos 6.5上不打印任何内容。适用于带有grep 2.6.3的Debian 6.0.8。
伊恩·艾伦
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.