1

当 I 时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 下给出结果。我如何测试,哪个语言环境对范围有效?
  • 我没想到每个测试用例中的所有字符都是大写的。AFAIUuc并且lc应该依赖于语言环境。在第一种情况下,我认为它们都会小写,但是使用语言环境我等待前六个字符大写,而其他字符则不是。唯一的情况是我等待所有字符大写,是第三个。我看到我在这里错过了一些重要的事情。糟糕,现在我从lc文档中找到:“否则,如果 EXPR 设置了 UTF-8 标志:Unicode 语义用于大小写更改。” UTF-8 标志始终设置在 my 上$string,因此在编写过程中得到了答案。

locale用于排序和\p{Word}匹配对我来说是可以接受的,但我仍然会使用一些提示:为什么不能\w按我的预期工作?

4

1 回答 1

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;

}

这确实是您应该如何处理语言环境排序规则。有关众多示例,另请参见此答案

于 2013-02-23T01:44:59.277 回答