ソースコード
以下で説明する書き換え関数のソースコードは、こちらから入手できます。
Java 7での更新
Pattern
JDK7用のSunの更新されたクラスには、驚くべき新しいフラグがありUNICODE_CHARACTER_CLASS
、すべてが再び正常に機能します。(?U)
パターンの内部に埋め込むことができるので、String
クラスのラッパーと一緒に使用することもできます。また、他のさまざまなプロパティの定義も修正されています。現在、UTS#18:Unicode Regular ExpressionsのRL1.2とRL1.2aの両方で、Unicode標準を追跡しています。これはエキサイティングで劇的な改善であり、開発チームはこの重要な取り組みに対して表彰されます。
Javaの正規表現Unicodeの問題
意味は- Javaの持つ問題の正規表現は、Perl 1.0 charclassエスケープということで\w
、\b
、\s
、\d
JavaでのUnicodeで動作するように拡張されていない-とそれらの相補体。これらの中で単独で、\b
特定の拡張されたセマンティクスを享受しますが、これら\w
はにも、Unicode識別子にも、Unicode改行プロパティにもマッピングされません。
さらに、JavaのPOSIXプロパティには次の方法でアクセスします。
POSIX syntax Java syntax
[[:Lower:]] \p{Lower}
[[:Upper:]] \p{Upper}
[[:ASCII:]] \p{ASCII}
[[:Alpha:]] \p{Alpha}
[[:Digit:]] \p{Digit}
[[:Alnum:]] \p{Alnum}
[[:Punct:]] \p{Punct}
[[:Graph:]] \p{Graph}
[[:Print:]] \p{Print}
[[:Blank:]] \p{Blank}
[[:Cntrl:]] \p{Cntrl}
[[:XDigit:]] \p{XDigit}
[[:Space:]] \p{Space}
それは物事が好きなことを意味するので、これは本当の混乱でAlpha
、Lower
とSpace
やるではない UnicodeへのJavaマップにAlphabetic
、Lowercase
またはWhitespace
プロパティ。これは非常に迷惑です。JavaのUnicodeプロパティのサポートは厳密に先駆的ですです。つまり、過去10年間に登場したUnicodeプロパティはサポートされていません。
ホワイトスペースについて適切に話すことができないことは、非常に迷惑です。次の表を検討してください。これらのコードポイントごとに、JavaのJ-results列と、Perlまたはその他のPCREベースの正規表現エンジンのP-results列の両方があります。
Regex 001A 0085 00A0 2029
J P J P J P J P
\s 1 1 0 1 0 1 0 1
\pZ 0 0 0 0 1 1 1 1
\p{Zs} 0 0 0 0 1 1 0 0
\p{Space} 1 1 0 1 0 1 0 1
\p{Blank} 0 0 0 0 0 1 0 0
\p{Whitespace} - 1 - 1 - 1 - 1
\p{javaWhitespace} 1 - 0 - 0 - 1 -
\p{javaSpaceChar} 0 - 0 - 1 - 1 -
あれ?
Unicodeによると、Javaホワイトスペースの結果のほぼすべてが「w̲r̲o̲n̲g」です。それは本当に大きな問題です。 Javaはめちゃくちゃになっていて、既存の慣習やUnicodeに従って「間違った」答えを出します。さらに、Javaでは実際のUnicodeプロパティにアクセスすることさえできません!実際には、Javaがサポートされていない任意の Unicodeの空白文字に対応することをプロパティを。
これらすべての問題の解決策など
これと他の多くの関連する問題に対処するために、昨日、これらの14の文字クラスエスケープを書き換えるパターン文字列を書き換えるJava関数を書きました。
\w \W \s \S \v \V \h \H \d \D \b \B \X \R
それらを、予測可能で一貫性のある方法でUnicodeに一致するように実際に機能するものに置き換えることによって。単一のハックセッションからのアルファプロトタイプにすぎませんが、完全に機能します。
短い話は、私のコードはこれらの14を次のように書き換えることです。
\s => [\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]
\S => [^\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]
\v => [\u000A-\u000D\u0085\u2028\u2029]
\V => [^\u000A-\u000D\u0085\u2028\u2029]
\h => [\u0009\u0020\u00A0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]
\H => [^\u0009\u0020\u00A0\u1680\u180E\u2000\u2001-\u200A\u202F\u205F\u3000]
\w => [\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]
\W => [^\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]
\b => (?:(?<=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])|(?<![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]))
\B => (?:(?<=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])|(?<![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]))
\d => \p{Nd}
\D => \P{Nd}
\R => (?:(?>\u000D\u000A)|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])
\X => (?>\PM\pM*)
考慮すべきいくつかの事柄...
そのために使用する\X
ものを定義Unicodeは今を参照すると、従来の書記素クラスタではなく、拡張書記素クラスタ後者はむしろ、より複雑になっているとして、。Perl自体は現在、より洗練されたバージョンを使用していますが、古いバージョンは、最も一般的な状況でも完全に機能します。編集:下部の補遺を参照してください。
何をする\d
かはあなたの意図によって異なりますが、デフォルトはUniodeの定義です。いつも欲しがっているわけではないのです\p{Nd}
が、時々、[0-9]
あるいはそうしている人もいます\pN
。
2つの境界定義、\b
および\B
は、\w
定義を使用するように特別に記述されています。
その\w
定義は、丸いものだけでなく括弧付きの文字をつかむため、過度に広範です。Unicode Other_Alphabetic
プロパティはJDK7まで利用できないため、これが最善の方法です。
境界を探索する
境界は、Larry Wall氏は、最初の造語以来、問題となっている\b
と\B
どのように理解する1987年のキーでのPerl 1.0バックのためにそれらの話をするための構文を\b
し、\B
それらについて2つの普及の神話を払拭するためにある仕事の両方:
- 彼らは常に
\w
単語の文字を探しているだけで、単語以外の文字は探していません。
- 彼らは文字列の端を特に探しません。
\b
境界の手段:
IF does follow word
THEN doesn't precede word
ELSIF doesn't follow word
THEN does precede word
そして、それらはすべて次のように完全に簡単に定義されています。
- 次の単語は
(?<=\w)
です。
- 単語の前にあり
(?=\w)
ます。
- フォローしていない単語は
(?<!\w)
です。
- 先行言葉がないです
(?!\w)
。
したがってため、IF-THEN
として符号化されるand
ED-一緒にAB
正規表現で、or
でありX|Y
、そしてので、and
より優先順位が高いor
単純です、AB|CD
。つまり\b
、境界は安全に次のように置き換えることができます。
(?:(?<=\w)(?!\w)|(?<!\w)(?=\w))
\w
適切な方法で定義されました。
(A
とC
コンポーネントが逆であるのは奇妙だと思うかもしれません。完璧な世界では、それを書くことができるはずですAB|D
が、しばらくの間、私はUnicodeプロパティの相互排除の矛盾を追っていました。私が世話をしてきましたが、ただし念のために境界に二重条件を残しました。これにより、後で余分なアイデアを得た場合に拡張性が高まります。)
以下のために\B
非境界、ロジックは次のとおりです。
IF does follow word
THEN does precede word
ELSIF doesn't follow word
THEN doesn't precede word
のすべてのインスタンスの\B
置き換えを許可:
(?:(?<=\w)(?=\w)|(?<!\w)(?!\w))
これは実際にどのよう\b
に\B
動作します。それらの同等のパターンは
\b
構成の使用((IF)THEN|ELSE)
は(?(?<=\w)(?!\w)|(?=\w))
\B
構成の使用((IF)THEN|ELSE)
は(?(?=\w)(?<=\w)|(?<!\w))
しかし、 AB|CD
Javaのような正規表現言語に条件付きパターンがない場合は特に、問題ありません。☹
実行ごとに110,385,408の一致をチェックするテストスイートを使用して、3つの同等の定義すべてを使用して境界の動作を既に確認しました。
0 .. 7F the ASCII range
80 .. FF the non-ASCII Latin1 range
100 .. FFFF the non-Latin1 BMP (Basic Multilingual Plane) range
10000 .. 10FFFF the non-BMP portion of Unicode (the "astral" planes)
しかし、人々はしばしば異なる種類の境界を望みます。彼らは空白と文字列の端を認識する何かを望んでいます:
- 左端 として
(?:(?<=^)|(?<=\s))
- 右端として
(?=$|\s)
JavaによるJavaの修正
私の他の回答で投稿したコードは、これと他のいくつかの便利な機能を提供します。これには、自然言語の単語、ダッシュ、ハイフン、アポストロフィの定義に加えて、もう少し多くの定義が含まれます。
また、ばかばかしいUTF-16サロゲートではなく、論理コードポイントでUnicode文字を指定することもできます。それがどれほど重要であるかを強調するのは難しいです!そしてそれは文字列の拡張のためだけです。
Java正規表現のcharclassが最終的にUnicodeで機能し、正しく機能するようにする正規表現のcharclass置換については、ここから完全なソースを 取得してください。 もちろん、好きなように使ってもかまいません。それを修正する場合は、ぜひ聞いてみたいですが、そうする必要はありません。かなり短いです。メインの正規表現書き換え関数の本質は簡単です。
switch (code_point) {
case 'b': newstr.append(boundary);
break; /* switch */
case 'B': newstr.append(not_boundary);
break; /* switch */
case 'd': newstr.append(digits_charclass);
break; /* switch */
case 'D': newstr.append(not_digits_charclass);
break; /* switch */
case 'h': newstr.append(horizontal_whitespace_charclass);
break; /* switch */
case 'H': newstr.append(not_horizontal_whitespace_charclass);
break; /* switch */
case 'v': newstr.append(vertical_whitespace_charclass);
break; /* switch */
case 'V': newstr.append(not_vertical_whitespace_charclass);
break; /* switch */
case 'R': newstr.append(linebreak);
break; /* switch */
case 's': newstr.append(whitespace_charclass);
break; /* switch */
case 'S': newstr.append(not_whitespace_charclass);
break; /* switch */
case 'w': newstr.append(identifier_charclass);
break; /* switch */
case 'W': newstr.append(not_identifier_charclass);
break; /* switch */
case 'X': newstr.append(legacy_grapheme_cluster);
break; /* switch */
default: newstr.append('\\');
newstr.append(Character.toChars(code_point));
break; /* switch */
}
saw_backslash = false;
とにかく、そのコードは単なるアルファリリースで、週末にハッキングしたものです。それはそのままではありません。
ベータ版では、次のことを行います。
本番リリースでは、javadocとJUnitテストスイートが必要です。gigatesterを含めることもできますが、JUnitテストとして作成されていません。
補遺
良い知らせと悪い知らせがあります。
良いニュースは、改良されたに使用する拡張書記素クラスタに非常に近い近似を得たということです。\X
悪いニュースは、そのパターンは次のとおりです:
(?:(?:\u000D\u000A)|(?:[\u0E40\u0E41\u0E42\u0E43\u0E44\u0EC0\u0EC1\u0EC2\u0EC3\u0EC4\uAAB5\uAAB6\uAAB9\uAABB\uAABC]*(?:[\u1100-\u115F\uA960-\uA97C]+|([\u1100-\u115F\uA960-\uA97C]*((?:[[\u1160-\u11A2\uD7B0-\uD7C6][\uAC00\uAC1C\uAC38]][\u1160-\u11A2\uD7B0-\uD7C6]*|[\uAC01\uAC02\uAC03\uAC04])[\u11A8-\u11F9\uD7CB-\uD7FB]*))|[\u11A8-\u11F9\uD7CB-\uD7FB]+|[^[\p{Zl}\p{Zp}\p{Cc}\p{Cf}&&[^\u000D\u000A\u200C\u200D]]\u000D\u000A])[[\p{Mn}\p{Me}\u200C\u200D\u0488\u0489\u20DD\u20DE\u20DF\u20E0\u20E2\u20E3\u20E4\uA670\uA671\uA672\uFF9E\uFF9F][\p{Mc}\u0E30\u0E32\u0E33\u0E45\u0EB0\u0EB2\u0EB3]]*)|(?s:.))
これはJavaでは次のように記述します。
String extended_grapheme_cluster = "(?:(?:\\u000D\\u000A)|(?:[\\u0E40\\u0E41\\u0E42\\u0E43\\u0E44\\u0EC0\\u0EC1\\u0EC2\\u0EC3\\u0EC4\\uAAB5\\uAAB6\\uAAB9\\uAABB\\uAABC]*(?:[\\u1100-\\u115F\\uA960-\\uA97C]+|([\\u1100-\\u115F\\uA960-\\uA97C]*((?:[[\\u1160-\\u11A2\\uD7B0-\\uD7C6][\\uAC00\\uAC1C\\uAC38]][\\u1160-\\u11A2\\uD7B0-\\uD7C6]*|[\\uAC01\\uAC02\\uAC03\\uAC04])[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]*))|[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]+|[^[\\p{Zl}\\p{Zp}\\p{Cc}\\p{Cf}&&[^\\u000D\\u000A\\u200C\\u200D]]\\u000D\\u000A])[[\\p{Mn}\\p{Me}\\u200C\\u200D\\u0488\\u0489\\u20DD\\u20DE\\u20DF\\u20E0\\u20E2\\u20E3\\u20E4\\uA670\\uA671\\uA672\\uFF9E\\uFF9F][\\p{Mc}\\u0E30\\u0E32\\u0E33\\u0E45\\u0EB0\\u0EB2\\u0EB3]]*)|(?s:.))";
¡Tschüß!