23

私のロケール(et_EE)では、次の[a-z]ことを意味します。

abcdefghijklmnopqrsšz

したがって、6つのASCII文字(tuvwxy)とエストニア語のアルファベット(ž)からの1つは含まれていません。まだ次のような正規表現を使用しているモジュールがたくさんあります

/\A[0-9A-Z_a-z]+\z/

私にとっては、ASCII英数字の範囲を定義するのは間違った方法のようであり、次のように置き換える必要があると思います。

/\A\p{PosixAlnum}+\z/

最初のものはまだ慣用的な方法と見なされていますか?または受け入れられた解決策?またはバグ?

または、最後の1つにいくつかの注意点がありますか?

4

5 に答える 5

8

古い Perl 3.0 の時代には、すべてが ASCII であり、Perl はそれを反映していました。\wと同じ意味[0-9A-Z_a-z]でした。そして、私たちはそれが好きでした!

ただし、Perl はもはや ASCII にバインドされていません。私が[a-z]書いたプログラムが英語以外の言語で動作しないと怒鳴られたので、しばらく前に使用をやめました。この世界には英語を話さない人が少なくとも数千人いることを知って、アメリカ人としての私の驚きを想像したに違いありません。

[0-9A-Z_a-z]とにかく、Perl にはより良い処理方法があります。セットを使用することも、[[:alnum:]]単に\w正しいことを行うものを使用することもできます。小文字のみを使用する必要がある場合は、[[:lower:]]代わりに[a-z](英語タイプの言語を想定) を使用できます。(Perl は、EBCDIC プラットフォームでも [az] が 26 文字の a、b、c、... z を意味するようにするために、ある程度の努力をします。)

ASCII のみを指定する必要がある場合は、/a修飾子。ロケール固有を意味する場合は、「使用ロケール」のレキシカル スコープ内で正規表現をコンパイルする必要があります。(正規表現パターンにのみ適用される /l 修飾子は避けてください。たとえば、's/[[:lower:]]/\U$&/lg' では、パターンはロケールを使用してコンパイルされます。しかし \U はそうではありません. これはおそらく Perl のバグと考えるべきでしょう. しかし, これが現在の仕組みです. /l 修飾子は実際には内部的な簿記のみを目的としており, 直接入力するべきではありません.) 実際には,プログラムへの入力時にロケール データを変換し、内部で Unicode を使用しながら、出力時に変換し直すことをお勧めします。あなたのロケールが最新の UTF-8 の 1 つである場合、5.16 の新機能「use locale ":not_characters"」

$word =~ /^[[:alnum:]]+$/   # $word contains only Posix alphanumeric characters.
$word =~ /^[[:alnum:]]+$/a  # $word contains only ASCII alphanumeric characters.
{ use locale;
  $word =~ /^[[:alnum:]]+$/;# $word contains only alphanum characters for your locale
}

さて、これはバグですか?プログラムが意図したとおりに動作しない場合、それは単純明快なバグです。本当に ASCII シーケンスが必要な場合[a-z]は、プログラマは修飾子を使用[[:lower:]]する必要があります。/a他の言語の小文字を含むすべての可能な小文字が必要な場合は、単純に を使用する必要があります[[:lower:]]

于 2012-08-13T02:03:09.340 に答える
5

考えられるロケールのバグ

あなたが直面している問題は、POSIX 文字クラス自体ではなく、クラスがロケールに依存しているという事実にあります。たとえば、regex(7) は次のように言います。

ブラケット式内で、「[:」と「:]」で囲まれた文字クラスの名前は、そのクラスに属するすべての文字のリストを表します...これらは wctype(3) で定義された文字クラスを表します。 ロケールは他のものを提供する場合があります。

強調は私のものですが、マニュアルページには、文字クラスがロケールに依存していると明確に書かれています。さらに、 wctype(3) は次のように述べています。

wctype() の動作は、現在のロケールの LC_CTYPE カテゴリに依存します。

つまり、ロケールで文字クラスが正しく定義されていない場合、それは特定のロケールに対して報告する必要があるバグです。一方、文字クラスが単に予期しない方法で文字セットを定義している場合、それはバグではない可能性があります。コード化する必要があるだけの問題かもしれません。

ショートカットとしての文字クラス

文字クラスは、セットを定義するためのショートカットです。確かに、あなたのロケール用に事前に定義されたセットに制限されることはなく、perlre(1) で定義された Unicode 文字セットを自由に使用するか、より高い精度が得られる場合は単純にセットを明示的に作成することができます。

あなたはすでにこれを知っているので、私は衒学的になろうとはしていません。ロケールを修正できない、または修正しない場合 (これが問題の原因です)、明示的なセットを使用する必要があることを指摘しているだけです。

便利なクラスは、ユース ケースで機能する場合にのみ便利です。そうでない場合は、船外に投げ出してください。

于 2012-08-12T20:42:32.633 に答える
1

それがまさにあなたが望むものであれば、使用[a-z]は間違っていません。

[a-zA-Z]しかし、英語の単語が、またはドイツ語の[a-zäöüßA-ZÄÖÜ]または名前のみで構成されていると信じるのは間違っています[A-Z][a-z]*

任意の言語または書記体系で単語が必要な場合 (2,300 の言語に対してテストされ、最も頻繁に使用される単語 50 K ごとに)、次のようなものを使用できます。

#!perl

use strict;
use warnings;
use utf8;

use 5.020;    # regex_sets need 5.18

no warnings "experimental::regex_sets";

use Unicode::Normalize;

my $word_frequencies = {};

while (my $line = <>) {
    chomp $line;
    $line = NFC($line);

    # NOTE: will catch "broken" words at end/begin of line
    #       and abbreviations without '.'
    my @words = $line =~ m/(
        (?[ \p{Word} - \p{Digit} + ['`´’] ])
        (?[ \p{Word} - \p{Digit} + ['`´’=⸗‒—-] ])*
    )/xg;
    
    for my $word (@words) {
        $word_frequencies->{$word}++;
    }
}

# now count the frequencies of graphemes the text uses

my $grapheme_frequencies = {};
for my $word (keys %{$word_frequencies}) {
    my @graphemes = m/(\X)/g;
    for my $grapheme (@grapheme) {
        $grapheme_frequencies->{$grapheme} 
            += $word_frequencies->{$word};
    }
}

\p{Word}より狭いチェックについては、Unicode 標準の定義を調べることができますhttps://unicode.org/reports/tr18/#word

word
    \p{alpha}
    \p{gc=Mark}
    \p{digit}
    \p{gc=Connector_Punctuation}
    \p{Join_Control}

これに基づいて\p{Word}、たとえばwordsラテン語のスクリプトで正規表現を定義できるようになりました。

# word:
    \p{Latin}    # \p{alpha}
    \p{gc=Mark}
    # \p{digit}  # we don't want numerals in words
    \p{gc=Connector_Punctuation}
    \p{Join_Control}

于 2021-08-12T18:49:56.853 に答える