1

Iuse localeの場合、ロケール (et_EE.UTF-8) の一部の文字が一致\wせず、理由がわかりません。

ASCII に加えて、エストニア語はさらに 6 つの文字を使用します。

õäöüšž

以下のテスト スクリプトでは、それら$stringを 3 つの追加の特殊文字ðŋц(エストニアのアルファベットには属さない) と共に使用しています。

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:  ðŋšžõäöüц
šžŋц
õäöüšžðŋц

私が期待したようではないのは何ですか?

  • 主な問題: 6 文字すべてに一致するuse localeことを望んでいましたが、結果はかなり奇妙です。なぜそのような試合?perlrecharclassから私は読んだ:\wšžŋц

255 を超えるコード ポイントの場合 ... \w は、この範囲の \p{Word} と同じように一致します。... 256 未満のコード ポイントの場合 ... ロケール ルールが有効な場合 ... \w は、プラットフォームのネイティブ アンダースコア文字と、ロケールが英数字と見なすものすべてに一致します。

したがって、\w255 を超える文字には一致しますが、「ロケールが英数字と見なすもの」には一致しません。なんで?同時に、ロケールの下での並べ替えは正常に機能し (ロケールなしでは機能しません)、結果ðŋšžõäöüцは正しい順序になり、適切な文字が適切に表現されていることが示されます。AFAIU、「ロケールが英数字と見なすものは何でも」それらを知らなければ、並べ替えはうまく機能しませんでした。または?

  • setlocalelocale-pragma の下でのみ結果が得られると思いました。どのロケールがスコープに有効かをテストするにはどうすればよいですか?
  • すべてのテストケースですべての文字が大文字になるとは思っていませんでした。AFAIUucでありlc、ロケールに依存する必要があります。最初のケースでは、それらはすべて小文字になると思っていましたが、ロケールを使用して、最初の 6 文字が大文字になるのを待っていましたが、他の文字はそうではありませんでした。すべての文字を大文字にして待っていたのは 3 番目でした。ここで重要な何かを見逃しているようです。おっと、今私はlcドキュメントから見つけました: 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 に答える