Unix特殊情况下敏感的UTF-8排序

时间:2011-11-08 14:34:44

标签: shell sorting unicode utf-8 collation

我找到了一个关于我的问题(shell - Different versions of UNIX sort handle case differently)的帖子,但它可以说出“相反”的答案。

我已经搞乱了LANG变量,但似乎无法找到实现我目标的值。

举例说明:

abc a
Abc d
Abc b
abc e
abæ g

需要分类到:

abc a
abc c
Abc b
Abc d
abæ g

不是这个(我现在得到的):

Abc b
Abc d
abc a
abc c
abæ g

而不是这个(这是我在不区分大小写时得到的结果):

abc a
Abc b
abc c
Abc d
abæ g

换句话说:我希望每列具有区分大小写的排序,其中大写起始字母的单词未在顶部排序,同一单词的大写/小写版本不会混合在一起,具体取决于第二列。

请注意,我需要UTF-8敏感排序(在这种情况下,我使用丹麦字母“æ”,它放在字母表中,如下所示:“...vwxyzæøå”)。

我使用以下方法对两列进行排序:

sort test.txt -k1,1 -k2,2

任何方式我都可以在不使用脚本的情况下做到这一点?

1 个答案:

答案 0 :(得分:6)

根据第二列的不同,您不希望第一列中的混合大小写混合在一起,但这正是不区分大小写的排序所给出的。它认为共享案例折叠的事物是相同的。

这组Unicode记录的排序:

abc a
Abc d
Abc b
abc e
abæ g

当然是这样的:

abæ g
abc a
Abc b
Abc d
abc e

那是因为第一个和第二个字母在所有五行中都是“相同”(,它们的casefolds是相同的),所以第一个不同的字母是第三个,这是一个æ的课程在c之前,这是其他四个记录的第三个字母。

对于其余的行,它们都具有相同的前三个字母,因此它们是第四个字母是决定性的,现在给出序列a,b,d,e。空格(通常)在Unicode排序中不重要,因为它是字母数字排序而不是代码点排序。我们只考虑这里的字母,除非它们一直相同,只考虑其他代码点。

这就是Unicode的排序方式。

除非您要求,否则Unicode校对算法不会注意丹麦语的排序。该代码点的默认DUCET条目将æ和å旁边的内容放在a旁边,ø旁边。 OED按此顺序对这些条目进行排序:

 allergist
 allergy
 Allerød
 allers
 allethrin

那是因为“Allerød”中的o遵循“过敏”中的g并且在allers中先于s。变音符号只有在其他一切都相同的情况下才有意义,所以假设的“过敏症”会出现在“Allerød”之前,假设的“过敏症”会跟随它而在“allers”之前。

这就是Unicode的排序方式。斯堪的纳维亚人讨厌它,因为他们认为它应该只做他们特殊的国家系统所做的事情,但Unicode并不偏向某种语言。如果你想要你的idiotsyncrasies,你必须使用区域设置排序。要获得这样的丹麦语区域特定类型:

abc a
Abc b
Abc d
abc e
abæ g

您需要使用指定的丹麦语言环境运行排序,而不是以破坏的POSIX方式运行,而是以Unicode方式运行。

首先,您必须放弃尝试使用 sort (1)。它更糟,然后无用:它不可靠和具有欺骗性。如果您有Unicode数据,那么您应该使用Unicode排序,无论是否为OED做了修改或为您的小村庄修改过。

要生成正常的Unicode排序,您必须使用:

#!/usr/bin/env perl
use strict;
use warnings;
use open qw(:std :utf8);
use utf8;

use Unicode::Collate;

my @lines = <<'End_of_Lines' =~ /\S.*\S\n/g;
    abc a
    Abc d
    Abc b
    abc e
    abæ g
End_of_Lines

my $collator = Unicode::Collate->new();
print $collator->sort(@lines);

虽然要获得区域设置限制的非默认排序,但您需要:

#!/usr/bin/env perl    
use strict;
use warnings;
use open qw(:std :utf8);
use utf8;

use Unicode::Collate::Locale;

my @lines = <<'End_of_Lines' =~ /\S.*\S\n/g;
    abc a
    Abc d
    Abc b
    abc e
    abæ g
End_of_Lines

my $collator = Unicode::Collate::Locale->new(locale => "da");    
print $collator->sort(@lines);

自Perl版本v5.6以来,Unicode::Collate模块已包含在标准中。 自Perl版本v5.14以来,Unicode::Collate::Locale模块已包含在标准中,但在早期版本中可以从CPAN轻松安装:

 $ sudo perl -MCPAN -e "install Unicode::Collate::Locale"

您必须使用Perl的原因是您无法信任供应商区域设置根据Unicode归类算法工作,无论是否进行区域设置修改。我从未见过两种不同的系统,它们以相同的方式工作,这意味着每对中至少有一个被破坏,也许两者都是。相比之下,无论您身在何处,都可以保证UCA 始终的行为方式相同。它并不关心您的终端可以显示什么。它不关心字体。它不关心你是否被重定向。它不关心你正在运行的shell。它并不关心你的格特鲁德姨妈是否恰好在一个月的第5个星期一运行代码。它只是工作,并且在每种情况下每次都以相同的方式工作。使用UCA。不接受任何替代品。

但仅仅因为您使用UCA并不意味着您需要接受默认排序。 UCA的设计非常适合剪裁。如果你想要一个区域设置排序,这很容易 - 如果有该区域设置的CLDR数据,它是非常简单的。如果你想做一些书籍和电影片头,或者姓氏比姓氏更强的人名,以及所有苏格兰麦克和麦克风名字在M-之前排序,但不管彼此如何,所有这些都是UCA非常容易。您可以想象的任何事情都可以完成,并且通常非常容易。重点是,对于UCA,您总是从一种行为开始,无论平台或偏见如何,都保证以完全相同的方式工作。这意味着当您想要将自己的自定义应用于它时,您可以依赖它的工作方式。没有这种保证,一切都会丢失。

您可以为符合UCA here的Unix sort (1)程序获得预先制作的命令行替换(好吧,等等)。当然,它并没有做领域,但确实做了很多。