9

以前の質問からロケールプラグマの下で単語の文字が一致しないのはなぜですか?ネストされた引用符を変更する方法UTF-8データを処理する場合\w、word-charとして信頼することはできず、Unicode文字プロパティを使用する必要があることを学びました\p{Word}。現在、ゼロ幅の単語境界\bもUTF-8(ロケールが有効になっている)では機能しないことがわかりましたが、Unicode文字プロパティに同等のものは見つかりませんでした。私はそれを自分で次のように構築できると思いました:(?<=\P{Word})(\p{Word}+)(?=\P{Word})、それはと同等でなければなりません\b(\w+)\b

以下のテストスクリプトには、2つの異なる正規表現をテストするための2つの配列があります。\bロケールが有効になっていない場合、最初のベースは正常に機能します。ロケールでも機能するように、境界をエミュレートする別のバージョンを作成し(?=\P{Word})ましたが、期待どおりに機能しません(スクリプトでも期待される結果を示しています)。

何が問題で、エミュレートされた正規表現を最初にASCIIで(またはロケールなしで)機能させる方法がわかりますか?

#!/usr/bin/perl

use 5.010;
use utf8::all;
use locale; # et_EE.UTF-8 in my case
$| = 1;

my @test_boundary = (  # EXPECTED RESULT:
  '"abc def"',         # '«abc def»'
  '"abc "d e f" ghi"', # '«abc «d e f» ghi»'
  '"abc "d e f""',     # '«abc «d e f»»'
  '"abc "d e f"',      # '«abc "d e f»'
  '"abc "d" "e" f"',   # '«abc «d» «e» f»'
  # below won't work with \b when locale enabled
  '"100 Естонiï"',     #  '«100 Естонiï»'
  '"äöõ "ä õ ü" ï"',   # '«äöõ «ä õ ü» ï»'
  '"äöõ "ä õ ü""',     # '«äöõ «ä õ ü»»'
  '"äöõ "ä õ ü"',      # '«äöõ «ä õ ü»'
  '"äöõ "ä" "õ" ï"',   # '«äöõ «ä» «õ» ï»'
);

my @test_emulate = (   # EXPECTED RESULT:
  '"100 Естонiï"',     # '«100 Естонiï»'
  '"äöõ "ä õ ü" ï"',   # '«äöõ «ä õ ü» ï»'
  '"äöõ "ä õ ü""',     # '«äöõ «ä õ ü»»'
  '"äöõ "ä õ ü"',      # '«äöõ "ä õ ü»'
  '"äöõ "ä" "õ" ï"',   # '«äöõ «ä» «õ» ï»'
);

say "BOUNDARY";
for my $sentence ( @test_boundary ) {
  my $quote_count = ( $sentence =~ tr/"/"/ );

  for ( my $i = 0 ; $i <= $quote_count ; $i += 2 ) {
    $sentence =~ s/
      "(                          # first qoute, start capture
        [\p{Word}\.]+?            # suva word-char
        .*?\b[\.,?!»]*?           # any char followed boundary + opt. punctuation
      )"                          # stop capture, ending quote
      /«$1»/xg;                   # change to fancy
  }
  say $sentence;
}

say "EMULATE";
for my $sentence ( @test_emulate ) {
  my $quote_count =  ( $sentence =~ tr/"/"/ );

  for ( my $i = 0 ; $i <= $quote_count ; $i += 2 ) {
    $sentence =~ s/
      "(                         # first qoute, start capture
      [\p{Word}\.]+?             # at least one word-char or point
      .*?(?=\P{Word})            # any char followed boundary 
      [\.,?!»]*?                 # optional punctuation
      )"                         # stop capture, ending quote
      /«$1»/gx;                  # change to fancy
  }
  say $sentence;
}
4

2 に答える 2

7

の位置の後の文字\bは句読点または(安全のために、それらのいずれにも一致しない"ことを再確認してください)のいずれかであるため、大文字と小文字が区別されます。したがって、次のようにエミュレートできます。\p{Word}\b\W\b

(?<=\p{Word})

私はPerlに精通していませんが、ここでテストした\wところ、エンコーディングがUTF-8に設定されている場合も(および\b)うまく機能するようです。

$sentence =~ s/
  "(
    [\w\.]+?
    .*?\b[\.,?!»]*?
  )"
  /«$1»/xg;

uPerl 5.14以降に移行すると、フラグ付きの文字セットをUnicodeに設定できます。


この一般的な戦略を使用して、文字クラスに対応する境界を構築できます。(\b単語境界の定義がの定義に基づいているように\w)。

Cキャラクタークラスにしましょう。文字クラスCに基づく境界を定義したいと思います。

C以下の構造は、現在の文字が文字クラス(と同等(\b\w))に属していることがわかっている場合に、前の境界をエミュレートします。

(?<!C)C

または後ろ(と同等\w\b):

C(?!C)

なぜネガティブルックアラウンド?ポジティブルックアラウンド(補完的な文字クラスを使用)は、前後に文字が存在する必要があることも表明するためです(少なくとも1の前後のをアサートします)。ネガティブルックアラウンドは、面倒な正規表現を書かずに文字列の開始/終了の場合を可能にします。


\B\wエミュレーションの場合:

(?<=C)C

同様に\w\B

C(?=C)

\Bはの正反対です\b。したがって、正/負のルックアラウンドを反転して効果をエミュレートできます。それはまた理にかなっています-非境界は、前後にもっと多くのキャラクターがいるときにのみ形成することができます。


その他のエミュレーション(cの補完文字クラスとしますC):

  • \b\W(?<=C)c
  • \W\bc(?=C)
  • \B\W(?<!C)c
  • \W\Bc(?!C)

スタンドアロン境界のエミュレーションの場合(と同等\b):

(?:(?<!C)(?=C)|(?<=C)(?!C))

そして、スタンドアロンの非境界(と同等\B):

(?:(?<!C)(?!C)|(?<=C)(?=C))
于 2013-02-18T18:25:24.650 に答える
5

ネガティブルックアラウンドを使用する必要があります。

(?<!\p{Word})(\p{Word}+)(?!\p{Word})

ポジティブルックアラウンドは、単語以外の文字が存在する必要があるため、文字列の最初または最後で失敗します。ネガティブルックアラウンドはどちらの場合でも機能します。

于 2013-02-18T18:24:15.847 に答える