当我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 '';
}
我尝试使用Perl 5.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
匹配我的所有六个字符,但结果šžŋц
非常奇怪。为何如此匹配?从perlrecharclass我读到:对于255以上的代码点... \ w与\ p {Word}匹配相同 在这个范围内。 ...对于256以下的代码点...如果是区域设置规则 实际上... \ w匹配平台的本机下划线字符 加上语言环境认为是字母数字的。
因此,\w
匹配255以上的字符,但不匹配“无论语言环境认为是字母数字”。为什么?同一时间在语言环境下排序工作正常(并且没有语言环境),结果ðŋšžõäöüц
是正确的顺序,表明正确表示了正确的字符。 AFAIU,如果不知道它们“无论语言环境认为是字母数字”,它就无法正常工作。或?
setlocale
仅在locale-pragma下给出结果。我如何测试哪个区域设置对范围有效? uc
和lc
应该依赖于区域设置。在第一种情况下,我认为他们都会低调,但使用现场我等前六个字符是上限,而其他人没有。只有我等待所有字符大写的情况,才是第三。我看到我错过了一些重要的事情。哎呀,现在我从lc
docs找到了:“否则,如果EXPR设置了UTF-8标志:Unicode语义用于案例更改。” UTF-8标志总是在我的$string
上设置,因此在写入时得到了答案。使用locale
进行排序和\p{Word}
进行匹配对我来说是可以接受的,但我仍然会使用一些提示:为什么\w
无法正常工作?
答案 0 :(得分:5)
请不要使用损坏的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;
}
这就是你应该如何处理语言环境排序规则。有关大量示例,另请参阅this answer。