たまりば

  パソコン・インターネット パソコン・インターネット  三鷹市 三鷹市

ハロウィー?ンの正規表現
2012年11月01日 01:54

ハロウィーンですね。
…あ、昔の1日って日の出から始まるんでまだeveだと思うんですよ。

それはともかく、ハロウィーンといえばJack-O'-Lanternですね。
これ自分は「ジャックオーランタン」って書くんですが、
真ん中が「オー」じゃなくて「オ」や「オウ」だったり、
ジャックのクとつながって「ジャッコーランタン」になったり、
もっと縮めて「ジャコランタン」ってのもありますね。
「ランタン」は「ランターン」派もいたり、
「・」で区切って「ジャック・オー・ランタン」にしたり、
あと「-O'-」の部分を無くしちゃった「ジャックランタン」って言い方もあるっぽいです。

こういう表記ゆれに対抗する手段といえば正規表現。
例えば「ハロウィン」と「ハロウィーン」にマッチさせたければ/ハロウィー?ン/のように。
ではJack-O'-Lanternのカナ表記すべてにマッチさせる正規表現はどうなるのか。
考えてみるとなかなか手ごたえがあって楽しかったので正規表現好きの方は下の解答を見る前にチャレンジしてみるといいと思います。

まず、上のパターンをまとめると、ありうるパターンは次の26種類。
ジャックオーランターン
ジャックオーランタン
ジャックオウランターン
ジャックオウランタン
ジャックオランターン
ジャックオランタン
ジャッコーランターン
ジャッコーランタン
ジャッコウランターン
ジャッコウランタン
ジャッコランターン
ジャッコランタン
ジャコーランターン
ジャコーランタン
ジャコウランターン
ジャコウランタン
ジャコランターン
ジャコランタン
ジャック・オー・ランターン
ジャック・オー・ランタン
ジャック・オウ・ランターン
ジャック・オウ・ランタン
ジャック・オ・ランターン
ジャック・オ・ランタン
ジャックランターン
ジャックランタン
また、注意すべきパターンとして、
「ジャコ」以外の形で「ッ」の省略は不可。例: ジャクオランタン
「・」を「O'」の片側だけに入れるのは不可。例: ジャックオ・ランタン
とすべきであろう。
ジャコ・ランタンが許されるかどうかは微妙。ジャックオ・ランタンに類するものとして不可としておく。
ジャック・ランタンは許されるべきな気もするが、今回は気づいたのが正規表現を考えちゃった後だったので…。

こういう条件のもとで考えてみます。
まず後半は簡単。
ランター?ン
少しづつ潰して行きます。「オ」「オー」「オウ」の部分はこれでいけます。
オ[ーウ]?
「ッ」の省略は複雑です。「O'」の両側の「・」はとりあえずおいておくとして、「クオー」と「コー」のORで考えます。「クオー」の時は「ッ」が必須、「コー」の時は省略可ですので、こう書けます。
(ックオ|ッ?コ)
最後に「・」です。あるなら両方に無ければいけない、そんなときには前に出てきたものにマッチする後方参照です。
(・?)オー?\1
これで1個めの「・」があるならそれを、無ければ空文字列をキャプチャし、それを2個めにマッチさせることができます。
あとはジャックランタンに対応するために「オー」系列を囲って「?」を付け、まとめたものがこちらです。
/ジャ(ック((・?)オ[ーウ]?\3)?|ッ?コ[ーウ]?)ランター?ン/
「[ーウ]」は「・」との兼ね合いで2箇所に分かれているそれぞれに付ける必要があります。それと括弧が増えたので後方参照の数字が変わっていることに注意。

さて、これで一応正しくマッチしますが、「[ーウ]」と「ッ」が2箇所にあるのがちょっと美しくないですね。
(11/17追記: 以下に書いてある正規表現は間違っていました。下記の追記参照)
こんな時には必殺のアトミックゼロ幅アサーションです。名前がカッコいいです。
アサーションという言葉の意味はいまいちよく分からないのですが、どうやら「前提条件のチェック」のような意味っぽいです。
ここでは「ッ」の省略可否のチェックに使います。
1文字先を読んで「コ」であるなら「ッ」を省略可。すなわち「「ッ」である」か「空文字列であり次が「コ」である」となります。こうです。
ジャ(ッ|(?=コ))
こうすることによりこの後の場合分けが不要になり、最終的にこうなります。
/ジャ(ッ|(?=コ))(ク(・?)オ?|コ)[ーウ]?\3ランター?ン/
「コ」が2回出てきていますが、最初の「(?=コ)」の部分は先読みに使っただけで実際にはマッチしないので、実際のマッチのためにもう1個必要になります。
これを除いて複数出てきている文字は無くなったので、美しくなったと思います。複雑で分かりにくくなったという批判は受け付けません。

ところでこれ、「(ク(・?)オ?|コ)」の部分でORの左側を通れば「・」または空文字列がキャプチャされますが右側では何もキャプチャされない気がします。
これを「\3」で呼び出しているのは果たして大丈夫なのだろうかとちょっと不安になりますが、とりあえず実験に使ったEmEditorでは成功するようです。

(11/17追記)
…えー、美しくなったとか何とか言ってますが、思いっきり間違ってました。
以下解説しますが、どこが間違ってるのか探すのも面白いと思うので答えを見る前に考えてみるとよいでしょう。
ちなみに実は書いてすぐ間違いに気付いたんですが、どうにかして直せないか考えてるうちにこんなに遅くなってしまいました。
結局、直してはみたものの美しくなくなってしまって、アトミックゼロ幅アサーションを使う意味が無くなってしまいました。
あと、一応直した表現を書いてたんですが、タブに開いたまま数日放っておいたらブラウザ再起動で消えてやる気ダウン。
まあそんなわけでどこが間違っていたというと、「(ク(・?)オ?|コ)」の部分。
これで「ク」「ク・オ」「クオ」「コ」にマッチするんですが、その次の「[ーウ]?」が「」「ウ」「ー」にマッチするせいで、「ジャックウランタン」などにマッチしてしまいます。
これをどうにか直そうとすると、このあたり複雑に入り組んでいるのでどうしても場合分けがいりそうです。
否定後読みを使って「[ーウ]?」を「ク」が前に無いときにだけマッチさせるか
/ジャ(ッ|(?=コ))(ク(・?)オ?|コ)((?<!ク)[ーウ]?)?\3ランター?ン/
「ク」だけ別扱いにするか
/ジャ(ッ|(?=コ))(ク|(ク(・?)オ|コ)[ーウ]?)\4ランター?ン/
どちらにせよ同じ文字が複数出現するのは避けられない上に、最初のものより文字数が長くなってしまいます。
ちょっと直すだけでジャック・ランター?ンにマッチするのが利点かなとも思ったんですが、
/ジャ(ッ|(?=コ))(ク(・?)|(ク(・?)オ|コ)[ーウ]?)\5ランター?ン/
最初のでも普通に可能でした。
/ジャ(ック((・?)(オ[ーウ]?\3)?)?|ッ?コ[ーウ]?)ランター?ン/

タグ :プログラム

  • Post time : 2012年11月01日 01:54│Comments(0)
    URL欄を実験的に消してる間に廃止されてしまいました。まあいいか。
     
    <ご注意>
    書き込まれた内容は公開され、ブログの持ち主だけが削除できます。
    <?=$this->translate->_('削除')?>
    ハロウィー?ンの正規表現
      コメント(0)