为什么单词字符(\ w)在使用locale pragma下不匹配?

时间:2013-02-13 21:54:36

标签: perl sorting unicode utf-8 locale

当我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下给出结果。我如何测试哪个区域设置对范围有效?
  • 我没想到在每个测试用例中所有字符都是大写的。 AFAIU uclc应该依赖于区域设置。在第一种情况下,我认为他们都会低调,但使用现场我等前六个字符是上限,而其他人没有。只有我等待所有字符大写的情况,才是第三。我看到我错过了一些重要的事情。哎呀,现在我从lc docs找到了:“否则,如果EXPR设置了UTF-8标志:Unicode语义用于案例更改。” UTF-8标志总是在我的$string上设置,因此在写入时得到了答案。

使用locale进行排序和\p{Word}进行匹配对我来说是可以接受的,但我仍然会使用一些提示:为什么\w无法正常工作?

1 个答案:

答案 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