色々考えるってのは楽しい。
無駄に長文(笑)。
「【正規表現01】日付の表現 - 名もないテクノ手」で、日付にマッチする正規表現を考えてみましょうーってことで、仕事のふとしたときに色々考えてみた!っていうかあんまり本文を読まないで考えていたので遠回りをした!(ぉぃ
本文中では『「できました! 『\d+月\d+日』ですね。カンタンじゃん!」』っていきなり\dとか使っているんですけど、
\dってのは0〜9の数字のことね。
あと、+ってのは前に書いてあることを1回以上ってこと。
最初に私が思いついたのは「[0-1][1-9]月[0-3][0-9]日」。
っていうか07月01日とかダメっすか。
ルールに書いていないからOKなんじゃね?とか思ったけど、まぁ普通に19月とかマッチするし!みたいな感じなのでダメみたいです。*1
えーっとそうすると面倒だなー(笑)。
月を書いてみる
まず月をただしくマッチさせるものを書かないとですね。
1月〜9月は[1-9]月でいいかな。10月・11月・12月は…えっと…どうしよう…。
難しく考えないで「|」パイプという、まぁ条件式でいうところのORみたいなものを使って書いておけばいいかな。(面倒くさがり屋)
([1-9]|10|11|12)月
日を書いてみる
あとは日付なんだけど、それぞれの月によって30日があったりなかったりしますよね。
「西向く士*2」(=2・4・6・9・11*3)は31日がないので、それとそれ以外に分けるんかな。あ、2月は29日までですね。
なので
- 29日までグループ
- 30日までグループ
- 31日までグループ
に分かれました。
- 29日までは「([1-9]|[1-2][0-9])日」ですね。
- 30日までは「([1-9]|[1-2][0-9]|30)日」ですね。
- 31日までは「([1-9]|[1-2][0-9]|30|31)日」ですね。わー簡単(笑)。
- 29日までのグループは2月しかないから「2月([1-9]|[1-2][0-9])日」ですね。
- 30日までのグループは4・6・9・11月なので「([469]|11)月([1-9]|[1-2][0-9]|30)日」ですね。
- 31日までのグループは1・3・5・7・8・10・12月なので「([13578]|10|12)月([1-9]|[1-2][0-9]|30|31)日」ですね。わー簡単(笑)。
まぁ本当なら10・12月は「1[02]月」とか30・31日は「3[01]日」書けるんだけど、見づらくなるから無視。*4
あとは連結すれば完成だよー。
(2月([1-9]|[1-2][0-9])日)|(([469]|11)月([1-9]|[1-2][0-9]|30)日)|(([13578]|10|12)月([1-9]|[1-2][0-9]|30|31)日)
ってかここまで書いておいてなんなんだけど、正規表現のマッチって先頭から判定するのかな?
だとすると最初に(2月([1-9]|[1-2][0-9])日)って書くと12月にもマッチしちゃうよね。あ、マッチしても良いのか。
でも2月の所に入ってくるなんて許せない!っていう純粋な人は
(([469]|11)月([1-9]|[1-2][0-9]|30)日)|(([13578]|10|12)月([1-9]|[1-2][0-9]|30|31)日)|(2月([1-9]|[1-2][0-9])日)
とかにすればいいのかな。今考えたんだけど。
ちょっと前に思いついたのは「バレンタインデーは2月14日」「クリスマスは12月25日」って例文があったとして、2月にマッチさせたいなら、2の数字の前には数字はいくらなんでもないハズ…ということで
^[0-9]2月
って書けばいいんじゃないかーって思ったの。結構アイデアとしては良くないっすか?
その後「[0-9]」は「\d」って書けることを思い出して、「^\d」にすればいいんじゃないか!って思ったけど、なんかそれは「\D」と「d」を「D」にすればいいらしい。なんだそりゃ。InDesign語か?とか思って、Twitterでつぶやいたら@chalcedonyさんが汎用性があることを教えてくれたので(Twitter / chalcedony: @jdash2000 汎用ですよー。だいたいどこでも ...;念のため調べたら普通にPerlの正規表現でも使えました^^;)結果としては
\D2月
になりました。まぁそれなら1月も同じですね。「\D1」ということで。
ということで正規表現は
(\D2月([1-9]|[1-2][0-9])日)|((\D1|[3578]|10|12)月([1-9]|[1-2][0-9]|30|31)日)|(([469]|11)月([1-9]|[1-2][0-9]|30)日)
ということになるのではないでしょうか。
エレガントな書き方の例
ま、まぁ「Twitter / chalcedony: (?!\d)*5
だから
(?<!\d)(([1-9]|1[0-2])月([1-9]|[12]\d)日
頭に数字以外があるときに「(([1-9]|1[0-2])月([1-9]|[12]\d)日」を判定してね!ってこと。
これでさっきの「バレンタインデーは2月14日」「クリスマスは12月25日」問題は解決しました。
これで1月1日〜29日、2月1日〜29日(略)12月1日〜29日はマッチされました。
あとは
- ([13578]|1[02])月3[01]日
- ([469]|11)月30日
で30日と31日を「西向く士」で分けて拾えばOKですね。(2月は除く)
*1:っていうかそういう月日を書く人の方を更正すべきだよな。システム的には。とかここまで書いていたら、コピペで作業しているとそういうことも結構あるだろうということで、そうした訳のわからん日付をマッチさせる正規表現の方が編集の現場では必要なのでは…とか思ってきたのだが、まぁ話を先に進めましょう。
*2:サムライ、とここでは読みます
*3:十一=士なので
*4:Perlの話ですがパイプを使うと30倍遅くなる話は「perl regex performance - qootas.org/blog」にもあるので、真の正規表現使いはパイプを減らした方が良いのでしょうけど…
*5:[1-9]|1[0-2])月([1- ...」見たら、なんだかすごいことしているなーくらいにしか思えないくらいのうまい月日の拾い方なので、参考にするといいかもしれんず。
(?<!\d)(([1-9]|1[0-2])月([1-9]|[12]\d)日|([13578]|1[02])月3[01]日|([469]|11)月30日)
解説すると、上の「日を書いてみる」で書いたみたいに並べるとわかるけど、どこの月にも29日までは確実にあるわけですよねー。
なのですべての月で
([1-9]|[12]\d)日
とかけるわけです。わかりづらい?
([1-9]|[12][0-9])日
って書けばわかりやすいかな?(少なくとも私はわかりやすい(笑))
最初の「?<!」ってのは「否定後読み」ってものらしいですよ。私はこのTweetで初めて知りました(笑)。if文みたいなこともできるのね。
詳しくは「InDesign CS3 正規表現一覧」を見てね。
「?<!\d」ってのは、数字ではない場合は無視してねーって意味になります。否定だから理解が面倒!((数字ではないってさっき「\D」って書いたのに…って思った人は「?<=\D」でもOKだと思うよ