Perl 的“标准字符串比较顺序”是啥?

Posted

技术标签:

【中文标题】Perl 的“标准字符串比较顺序”是啥?【英文标题】:What is Perl's "standard string comparison order"?Perl 的“标准字符串比较顺序”是什么? 【发布时间】:2010-12-13 05:33:44 【问题描述】:

这真的是一个双重问题,我的两个最终目标都有答案:

就机制而言,标准字符串比较顺序是什么? 有什么更好的名称可以让我更新文档?

sort 的 Perl 文档说,没有块,sort 使用“标准字符串比较顺序”。但是这个命令是什么?它应该有一个更好的名字。对于这个问题,我特指locale 无效的情况,因为它定义了它自己的顺序。

在过去的几年里,我们通常将标准排序顺序称为“按ASCII 顺序”。它在Learning Perl 和许多其他书籍中。但是,该术语已过时。 Perl 从 5.6 开始就支持 Unicode。谈论 ASCII 是老派。因为 Perl 也支持 Unicode,所以它知道字符串。在 sv.c 中,Perl_sv_cmp 知道 localebytes 和 UTF-8。前两个很容易。但我对第三个没有信心。

/*
=for apidoc sv_cmp

Compares the strings in two SVs.  Returns -1, 0, or 1 indicating whether the
string in C<sv1> is less than, equal to, or greater than the string in
C<sv2>. Is UTF-8 and 'use bytes' aware, handles get magic, and will
coerce its args to strings if necessary.  See also C<sv_cmp_locale>.

=cut
*/

当 Perl 使用 UTF-8 进行排序时,它真正排序的是什么?字符串编码的字节,它代表的字符(可能包括标记?),还是其他?我认为这是 sv.c 中的相关行(提交 7844ec1 的第 6698 行):

 pv1 = tpv = (char*)bytes_to_utf8((const U8*)pv1, &cur1);

如果我没看错(使用我生锈的 C),pv1 被强制转换为八位字节,转换为 UTF-8,然后被转换为字符(在 C 意义上)。我认为这意味着它是按 UTF-8 编码排序的(即 UTF-8 用来表示代码点的实际字节)。另一种说法是它不对字素进行排序。我想我几乎已经说服自己我没看错,但你们中的一些人比我更了解这一点。

由此,下一个有趣的行是 6708:

 const I32 retval = memcmp((const void*)pv1, (const void*)pv2, cur1 < cur2 ? cur1 : cur2);

在我看来,曾经有pv1pv2,它们被强制转换为char *,现在只是逐字节比较,因为它们被强制转换为void *memcmp 会发生这种情况吗,看起来它只是根据我迄今为止阅读的各种文档比较位?同样,我想知道我在从 bytes->utf8->char->bytes 的旅程中缺少什么,比如可能是 Unicode 规范化步骤。在 utf8.c 中查看 Perl_bytes_to_utf8 并不能帮助我回答这个问题。

作为旁注,我想知道这是否与Unicode Collation Algorithm 相同?如果是,为什么Unicode::Collate 存在?从表面上看,我不认为 Perl 的 sort 处理规范等价。

【问题讨论】:

有一种简单的方法可以查看它们是否按字节对 UTF-8 序列进行排序:如果这样做,您会得到 A (自言自语)是的,这就是我在所有语言环境都设置为“C”时观察到的情况。似乎证实了你对源码的分析。 这不是我想观察的,这就是我要观察的。这种排序顺序从不与语言相关,即使对于英语也是如此(几十年来一直如此,早在 Unicode 之前)。我想它甚至存在的唯一原因是对所有字符串有一个总顺序,开销很小。话虽如此,您问题背后的真正问题是cmp 的默认行为,因为这决定了字符串的排序顺序。 我会问马库斯库恩:cl.cam.ac.uk/~mgk25/unicode.html 嗯,“自然”排序顺序是语言环境的用途,因为语言在字符顺序上存在分歧。 sort() 从来都不是关于自然秩序的,它展示了几十年的遗产和在 50 年代和 60 年代似乎正确的决定。先进技术的外星人可能永远不会造访像这样一个愚蠢的死水星球。 :) 【参考方案1】:

UTF-8 具有这样的特性,即根据字节值对 UTF-8 字符串进行逐字节排序与根据代码点编号逐代码点对其进行排序的排序相同。也就是说,我不用看就知道 U+2345 的 UTF-8 表示在字典上是 U+1234 的 UTF-8 表示之后。

至于规范化,Perl 核心对此一无所知。要在不同形式之间进行准确排序和比较,您需要通过Unicode::Normalize 运行所有字符串并将它们全部转换为相同的规范化形式。我无法评论哪个最适合任何特定目的,主要是因为我不知道。

此外,如果使用了 locale 杂注,则排序和 cmp 会受到影响;它使用 POSIX 排序规则。将use locale、8 位语言环境和 unicode 一起使用会导致灾难,但使用 use locale、UTF-8 语言环境和 unicode 应该会很有用。我不能说我试过了。无论如何,perllocale 和 perlunicode 中有大量信息。

【讨论】:

好的,我想这就是我需要的那种确认。我认为这就是它的工作原理,但我不确定。找个时间来开会,我可以给你买啤酒。 :) 我一直想参加一些会议,但我的日程安排通常让我工作到晚上 7 点或更晚,所以我通常不得不错过他们。我会努力解决的。 这是一个我不知道的非常有趣的事实。对我来说,这似乎是一个明智的设计决定! (事后看来很明显,但是嘿,大多数明智的决定都是。) @briandfoy 如果我是你,我绝不会调用代码点排序 lexicographic【参考方案2】:

我无法回答整个问题,所以让我只回答一个问题:

    const I32 retval = memcmp((const void*)pv1, (const void*)pv2, cur1 < cur2 ? cur1 : cur2);

...看起来曾经有pv1pv2,它们被强制转换为char *,现在只是逐字节比较,因为它们被强制转换为void *memcmp会发生这种情况吗

差不多。 memcmpstrcmp的主要区别是:

    strcmp 将在看到 NULL(即 '\0')时停止,并且 Perl 允许标量嵌入 NULLs memcmp 的运行速度通常比 strcmp 快一点

但除此之外,您将获得相同的结果。

【讨论】:

以上是关于Perl 的“标准字符串比较顺序”是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Perl 中字符串的最快校验位例程是啥?

在 Perl 中将文件转换为字符串的最佳方法是啥?

使用 Perl 检查数据数组中重复项的最有效方法是啥?

如何在 Perl 中比较两个字符串?

文本文件中的 Perl 字符串比较

谁能帮我注释一小段perl程序,尤其是if语句判断的是啥啊,while (<IN>) 是自动读一个字符串吗,存在哪里