当我use locale
时,我的区域设置中的一些字符(et_EE.UTF-8(与w
不匹配,我看不出有任何原因。
除了ASCII,爱沙尼亚语还使用了六个字符:
õäüšž
在下面的测试脚本中,我在$string
中使用了它们,并添加了三个额外的特殊字符ðŋц
(不属于爱沙尼亚字母表(。
use feature 'say';
use POSIX qw( locale_h );
{
use utf8;
my $string = "õäöüšž ðŋц";
binmode STDOUT, ":encoding(UTF-8)";
say "nothing";
say 'LOCALE: ', setlocale(LC_CTYPE), ' ', setlocale(LC_COLLATE);
say 'UC: ', uc( $string );
say 'SORT: ', sort( split(//, $string) );
say $string =~ m/w/g;
say $string =~ m/p{Word}/g;
say '';
}
{
use utf8;
use locale;
binmode STDOUT, ":encoding(UTF-8)";
my $string = "õäöüšž ðŋц";
say "locale";
say 'LOCALE: ', setlocale(LC_CTYPE), ' ', setlocale(LC_COLLATE);
say 'UC: ', uc( $string );
say 'SORT: ', sort( split(//, $string) );
say $string =~ m/w/g;
say $string =~ m/p{Word}/g;
say '';
}
{
use utf8::all;
my $string = "õäöüšž ðŋц";
say "utf8::all";
say 'LOCALE: ', setlocale(LC_CTYPE), ' ', setlocale(LC_COLLATE);
say 'UC: ', uc( $string );
say 'SORT: ', sort( split(//, $string) );
say $string =~ m/w/g;
say $string =~ m/p{Word}/g;
say '';
}
{
use utf8::all;
use locale;
my $string = "õäöüšž ðŋц";
say "utf8::all + locale";
say 'LOCALE: ', setlocale(LC_CTYPE), ' ', setlocale(LC_COLLATE);
say 'UC: ', uc( $string );
say 'SORT: ', sort( split(//, $string) );
say $string =~ m/w/g;
say $string =~ m/p{Word}/g;
say '';
}
我尝试了Perl5.10.1和5.14.2,它们都给了我这样的输出:
nothing
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: äðõöüŋšžц
õäöüšžðŋц
õäöüšžðŋц
locale
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: ðŋšžõäöüц
šžŋц
õäöüšžðŋц
utf8::all
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: äðõöüŋšžц
õäöüšžðŋц
õäöüšžðŋц
utf8::all + locale
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: ðŋšžõäöüц
šžŋц
õäöüšžðŋц
什么不是我所期望的?
- 主要问题:在
use locale
下,我希望w
能匹配我所有的六个字符,但结果šžŋц
很奇怪。为什么会有这样的比赛?从perlrecharlass我读到:
对于255以上的代码点。。。\w匹配项与\p{Word}匹配项相同在这个范围内。。。对于256以下的代码点。。。如果区域设置规则实际上。。。\w与平台的本机下划线字符匹配再加上语言环境认为是字母数字的内容。
因此,w
匹配255以上的字符,但不匹配"区域设置认为是字母数字的任何字符"。为什么?在区域设置下进行同时排序可以很好地工作(没有区域设置则不行(,结果ðŋšžõäöüц
是正确的顺序,这表明有正确的字符被正确地表示。AFAIU,如果不知道它们"无论语言环境认为是字母数字",排序就无法正常工作。或
- 我认为
setlocale
只在locale pragma下给出结果。如何测试哪个区域设置对作用域有效 - 我没想到在每个测试用例中所有字符都是大写的。AFAIU
uc
和CCD_。在第一种情况下,我认为它们都是小写的,但使用locale,我等待前六个字符是大写的,而其他字符则不是。我等待所有字符大写的唯一情况是第三个。我发现我错过了一些重要的事情。哎呀,现在我从lc
文档中发现:"否则,如果EXPR设置了UTF-8标志:则使用Unicode语义进行大小写更改。"UTF-8标志总是设置在我的$string
上,所以在编写过程中得到了答案
使用locale
进行排序,使用p{Word}
进行匹配对我来说是可以接受的,但我仍然会使用一些提示:为什么w
不能像我预期的那样工作?
请不要使用损坏的use locale
杂注。
请,请,请使用Unicode::Collate::Locale
进行区域设置排序。它使用CLDR规则,并且是完全可移植的,并且不依赖于不可靠的损坏的POSIX区域设置,这些区域设置根本不起作用。
如果你按代码点排序,你会得到一些废话,但如果你使用用爱沙尼亚语言环境构建的Unicode::Collate::Locale
对象进行排序,你得到了一些合理的东西:
Codepoint sort: äðõöüŋšžц
Estonian sort: ðŋšžõäöüц
此外,当您进行这种原始代码点排序时,您会受到规范化问题的严重影响。考虑:
NFC/NFD sort by codepoint is DIFFERENT
NFC Codepoint sort: äðõöüŋšžц
NFD Codepoint sort: äõöšüžðŋц
NFC/NFD sort in estonian is SAME
NFC Estonian sort: ðŋšžõäöüц
NFD Estonian sort: ðŋšžõäöüц
这是制作所有这些的演示程序。
#!/usr/bin/env perl
#
# et-demo - show how to handle Estonian collation correctly
#
# Tom Christinansen <tchrist@perl.com>
# Fri Feb 22 19:27:51 MST 2013
use v5.14;
use utf8;
use strict;
use warnings;
use warnings FATAL => "utf8";
use open qw(:std :utf8);
use Unicode::Normalize;
use Unicode::Collate::Locale;
main();
exit();
sub graphemes(_) {
my($str) = @_;
my @graphs = $str =~ /X/g;
return @graphs;
}
sub same_diff($$) {
my($s1, $s2) = @_;
no locale;
if (NFC($s1) eq NFC($s2)) {
return "SAME";
} else {
return "DIFFERENT";
}
}
sub stringy {
return join("" => @_);
}
sub cp_sort {
no locale;
return sort @_;
}
sub et_sort {
state $collator = # we want Estonian here:
Unicode::Collate::Locale->new(locale => "et");
return $collator->sort(@_);
}
sub main {
my $orig = "õäöüšž ðŋц";
say " Codepoint sort: ", cp_sort(graphemes($orig));
say " Estonian sort: ", et_sort(graphemes($orig));
my $nfc = NFC($orig);
my $nfc_cp_sort = stringy cp_sort(graphemes($nfc));
my $nfc_et_sort = stringy et_sort(graphemes($nfc));
my $nfd = NFD($orig);
my $nfd_cp_sort = stringy cp_sort(graphemes($nfd));
my $nfd_et_sort = stringy et_sort(graphemes($nfd));
say "NFC/NFD sort by codepoint is ",
same_diff($nfc_cp_sort, $nfd_cp_sort);
say "NFC Codepoint sort: ", $nfc_cp_sort;
say "NFD Codepoint sort: ", $nfd_cp_sort;
say "NFC/NFD sort in estonian is ",
same_diff($nfc_et_sort, $nfd_et_sort);
say "NFC Estonian sort: ", $nfc_et_sort;
say "NFD Estonian sort: ", $nfd_et_sort;
}
这确实是处理区域设置排序规则的方式。请参阅此答案,了解更多示例。