質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Unicode

Unicodeはエンコーディングの標準規格です。1つの文字コード体系で多国語の表現を可能にすることを目指して作られています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

1回答

3387閲覧

Unicode 拡張書記素クラスタの正規表現パターンは何でしょうか?

sounisi5011

総合スコア697

Unicode

Unicodeはエンコーディングの標準規格です。1つの文字コード体系で多国語の表現を可能にすることを目指して作られています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

1グッド

0クリップ

投稿2016/01/19 16:40

編集2016/01/19 16:55

PCRE正規表現エンジンを採用しているPHPのドキュメントには、以下の記述が存在します。

PHP: Unicode 文字プロパティ - Manual

\X は、Unicode 拡張書記素クラスタにマッチします。 拡張書記素クラスタとは、ひとつあるいは複数の Unicode 文字の組み合わせで単一のグリフを構成するものです。 事実上、これは Unicode 版の . だと考えてかまいません。 その文字をレンダリングするために実際に何文字が使われているかは考えずに、ひとつの合成文字に対応します。

8.32 より古いバージョンの PCRE (これは、組み込みの PCRE ライブラリを使っている場合には PHP 5.4.14 より前のバージョンにあたります) では、 \X は (?>\PM\pM*) と等価です。 つまり、記号 (mark) プロパティの付いていない文字と、その後に続く 0 以上の 記号プロパティ付きの文字にマッチし、その並びをアトミック (atomic) な まとまりとして取り扱います。記号プロパティ付きの文字とは、アクセント記号などの 直前の文字に対して影響するようなもののことです。

この内容によると、拡張書記素クラスタにマッチするパターンの\Xは、PCRE 8.32以降、(?>\PM\pM*)ではないパターンであると解釈できます。
つまり、改善されたものと考えられ、(?>\PM\pM*)は拡張書記素クラスタを表現するパターンとして、厳密には正しくないものと予想されます。

もしそうであるならば、拡張書記素クラスタと厳密に等価な、正しい正規表現パターンは何なのでしょうか?


JavaScriptの正規表現や、任意のUnicode 一般カテゴリプロパティを除外した拡張書記素クラスタのパターンを記述したいので、\Xは利用できません。
なので、\Xを利用せずに、拡張書記素クラスタの正規表現パターンを解答していただけると、ありがたいです。

ikuwow👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

unicodeの仕様でテキストを文字に分割するアルゴリズムが定まったので、これに基づくものに変更したようです。丁寧に正規表現に置き換えればいいと思いますがかなり大変そうですね。

Grapheme Cluster Boundaries

unicodeには合字で表す文字がアルファベット+アクセント記号のようなパターンの他に、ハングル文字の字母を組み合わせるものや、国旗の絵文字があって、それに対応したのが主な変更です。

日本語のわかりやすい解説もあったので参考にしてください。

hydroculのメモ:Unicodeのgrapheme cluster (書記素クラスタ)

こんな感じにしてあとは文字クラスの部分を調べて補えばいけるとは思います。(すいません、力尽きました)

PHP

1$Control = '\x{00}-\x{09}\x{0b}-\x{0c}\x{0e}-\x{1f}\x{7f}-\x{9f}'; # 略 2$ExtendOrSpacingMark = '\x{0300}-\x{036f}\x{0483}-\x{489}'; # 略 3$L = '\x{1100}-\x{115f}\x{a960}-\x{a97c}'; 4$V = '\x{1160}-\x{11a7}\x{d7b0}-\x{d7c6}'; 5$T = '\x{11a8}-\x{11ff}'; # 略 6$LV = '\x{ac00}\x{ac1c}'; # 略 7$LVT = '\x{ac01}-\x{ac1b}'; # 略 8$RegionalIndicator = '\x{1F1E6}-\x{1F1FF}'; 9$Other = '\x{20}-\x{7e}\x{a0}-\x{ac}\x{ae}-\x{2ff}\x{3030}-\x{3098}'; # 略 10 11$egc_re = '(?>' 12 . '\x{0d}\x{0a}?' 13 . '|' 14 . '\x{0a}' 15 . '|' 16 . "[$Control]" 17 . '|' 18 . "[$ExtendOrSpacingMark]+" 19 . '|' 20 . "[$L]+(?:[$LVT]|[$LV][$V]*[$T]*|[$V]+[$T]*|[$T]+)?[$ExtendOrSpacingMark]*" 21 . '|' 22 . "[$V]+[$T]*[$ExtendOrSpacingMark]*" 23 . '|' 24 . "[$T]+[$ExtendOrSpacingMark]*" 25 . '|' 26 . "[$LV][$V]*[$T]*[$ExtendOrSpacingMark]*" 27 . '|' 28 . "[$LVT][$T]*[$ExtendOrSpacingMark]*" 29 . '|' 30 . "[$RegionalIndicator]+" 31 . '|' 32 . "[$Other][$ExtendOrSpacingMark]*" 33 . ')'; 34 35printf("%s\n", $egc_re); 36 37 38preg_match_all('/'.$egc_re.'/u', 'abcほげ', $matches); 39var_dump($matches);

投稿2016/01/20 07:33

crhg

総合スコア1175

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sounisi5011

2016/01/20 17:36

参考資料を提示しただき、ありがとうございます! 境界定義についてよく読んだのですが、例えばLの次にLが続いた場合、その続いたLについてもまた同様のルールを適用しなくてはなりません。 これは、言ってみれば再帰的なネスト構造のような状態であり、これを正規表現で表現することは困難であると判断しました。 PHPの場合は再帰的パターンの構文でどうにかなる…かも知れませんが、JavaScriptの場合は再帰的パターンの構文を正規表現では表現できません。 単純に、文字列処理で対応した方が良さそうです。 具体的なコードが書け次第、ベストアンサーとして選択したいと思います。
crhg

2016/01/21 00:01

Lの次にLのような奴はただの繰り返しなので普通の正規表現でいけますよ。任意の数のカッコを数えてバランスとかそういうのが正規表現では出来ないのは有名ですが今回はそのような場合ではないです。
sounisi5011

2016/01/21 06:42 編集

Grapheme Cluster Boundary RulesのGB6は、Lの次にL、V、LV、LVTが続く場合に境界としない、というルールとなっています。これを単純なパターンとして書くと以下のようになるのですが… [L](?:[L]|[V]|[LV]|[LVT]) 続いているLにも同様のルールが適用されることになり、以下のように書かなくてはなりません。 [L](?:[L](?:[L]|[V]|[LV]|[LVT])|[V]|[LV]|[LVT]) そして、この中にもLが含まれているので、また適用されることになり…と、仕様に順序した場合、無限にネストしたパターンを書く必要があると考えます。 再帰的パターンの構文を使用すればこの問題は回避できるかもしれませんが、JavaScriptに再帰的パターンの構文は存在しませんし、 そもそも、本来の正規表現には再帰的パターンなどはありません。 このため、正規表現で表現するのは困難であると判断しました。
crhg

2016/01/21 07:23

それはよく見ると繰り返されるのは[L]だけなので、 [L]+(?:[V]|[LV]|[LVT])? です。VやLVはさらに後に続くので、それらを考慮してまとめたのが . "[$L]+(?:[$LVT]|[$LV][$V]*[$T]*|[$V]+[$T]*|[$T]+)?[$ExtendOrSpacingMark]*" の行です。
sounisi5011

2016/01/21 07:45

…ああ、確かに。再帰無しでも表現できそうですね。 今一度、正規表現によるパターンを考えてみます…
sounisi5011

2016/01/21 07:58 編集

すみません、お聞きしたいのですがよろしいでしょうか。 仕様「Grapheme Cluster Boundaries」に含まれている「Table 1b」に、Extended grapheme clusterの正規表現パターンが記述されているように思います。 試しに、このパターンを図にしてGrapheme Cluster Boundary Rulesと比較したのですが、このパターンに矛盾は無いように思います。 少なくとも、私には矛盾を確認できませんでした。 頑張って書いていただいたパターンを否定するようで申し訳ないのですが、 仕様にあるこのパターンは正しいものなのでしょうか? 一連の作業、及びリンクは、以下のツイートに返信する形でまとめています: https://twitter.com/sounisi5011Prog/status/690069149654065153
crhg

2016/01/21 09:15

これのことだと思いますが ( CRLF | Prepend* ( RI-sequence | Hangul-Syllable | !Control ) ( Grapheme_Extend | SpacingMark )* | . ) これだと!ControlにはCR,LFが含まれてしまうので単独のCR,LFの前後で切れるというGB4,GB5に違反してしまいますね。おそらく間違いでしょう。そこだけ修正すればいいと思います。
sounisi5011

2016/01/21 09:31

> これだと!ControlにはCR,LFが含まれてしまうので単独のCR,LFの前後で切れるというGB4,GB5に違反してしまいますね コメント後にその問題に気がついたのですが、やはり違反していましたか。ありがとうございます! PHPとJavaScript用の正規表現の目処が立ったので、実際のパターンを生成して記載したうえで、こちらの解答をベストアンサーとさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問