微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Bash 字符串字典序比较不一致

如何解决Bash 字符串字典序比较不一致

Bash manual section 6.4 将 [[ string1

如果 string1 在当前语言环境中按字典顺序排在 string2 之后,则为真。

我使用的是标准英语 Linux,并希望我当前的语言环境是 ASCII,其中句点 [.] 在字典上小于 [0-9A-Za-z]。但是,请查看以下内容

$ echo $BASH_VERSION
4.3.11(1)-release
$ [[ "." < "1" ]] && echo "yes"
yes
$ [[ "A" < "B" ]] && echo "yes"
yes
$ [[ ".A" < "1B" ]] && echo "yes"
$

第一次和第二次比较与ASCII表一致,但为什么第三次是错误的?这个字典排序顺序到底是什么?

这里是语言环境的输出

$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

解决方法

这与您的外壳没有太大关系。要执行 .A1B 的依赖于语言环境的字典比较,bash 只需调用 strcoll(".A","1B"),并解释返回值,就这些。

    {
#if defined (HAVE_STRCOLL)
      if (shell_compatibility_level > 40 && flags & TEST_LOCALE)
    return ((op[0] == '>') ? (strcoll (arg1,arg2) > 0) : (strcoll (arg1,arg2) < 0));
      else
#endif
    return ((op[0] == '>') ? (strcmp (arg1,arg2) > 0) : (strcmp (arg1,arg2) < 0));
    }

(复制自test.c

以上摘录还表明,为了在不改变区域设置的情况下强制执行 byte-by-byte comparison,需要将 shell 兼容级别更改为 40(代表 4.0,即bash 默认情况下的行为方式与您预期的一样)。

$ shopt -s compat40
$ [[ .A < 1B ]] && echo yes
yes
$ 

现在,至于你的问题(第一和第二个比较与 ASCII 表一致,但为什么第三个是错误的?这个字典排序顺序到底是什么?),好吧,这是你的语言环境的整理顺序显然。在 What Collation is NOT 下,UCA 规范说:

通常在串联或子字符串操作下不保留整理顺序。

例如,x 小于 y 的事实并不意味着 x + z 小于 y + z,因为字符可能会在子字符串或连接边界之间形成收缩。总结:

x 并不意味着 xz
x 并不意味着 zx
xz 并不意味着 x
zx 并不意味着 x

我认为这证实了这不是错误而是功能。

,

UTF-8 整理顺序不会像传统的 ASCIIbetical 整理那样逐个字符进行。它使用 multi-level comparison,其中某些类型的差异优先于其他类型即使它们出现在字符串的后面。在这种情况下,您看到的“基本字符”顺序(“A”

为了解决语言敏感排序的复杂性,采用了多级比较算法。在比较两个单词时,最重要的特征是基本字母的同一性——例如,A 和 B 之间的差异。如果基本字母不同,通常会忽略重音差异。如果基本字母或其重音不同,则通常会忽略大小写差异(大写与小写)。标点符号的处理各不相同。在某些情况下,标点符号被视为基本字母。在其他情况下,如果有任何基本、重音或大小写差异,则应忽略它。 [...]

以下示例显示了标点符号与“基本字符”的优先级:

$ printf '%s\n' {,.,-}{,1,A,AB,B,BA} | LANG=en_US.UTF-8 sort
-
.
-1
.1
1
-A
.A
A
-AB
.AB
AB
-B
.B
B
-BA
.BA
BA

请注意,标点符号仅对打破包含相同基本字符的行之间的联系很重要。您还可以看到涉及大写和重音的类似效果:

printf '%s\n' {a,B}{A,Å,B} | LANG=en_US.UTF-8 sort
aA
AA
aÅ
AÅ
aB
AB
BA
BÅ
BB

请注意,第二个字符上的重音优先级高于第一个字符的大写(字符串中任何地方的标点符号的优先级都低于两者)。

(当然,除此之外还有很多其他的并发症。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。