Python で正規表現を構文解析だけした結果(内部表現)を得る方法がないかと思って調べてみたところ、sre_parseを使えばできることが分かったのでメモ。
例えば、メールアドレスにマッチする正規表現(^[a-zA-Z0-9_+-]+(.[a-zA-Z0-9_+-]+)*@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$
)を parse すると[(AT, AT_BEGINNING), (MAX_REPEAT, (1, MAXREPEAT, [(IN, [(RANGE, (97, 122)), (RANGE, (65, 90)), (RANGE, (48, 57)), (LITERAL, 95), (LITERAL, 43), (LITERAL, 45)])])), (MAX_REPEAT, (0, MAXREPEAT, [(SUBPATTERN, (1, 0, 0, [(ANY, None), (MAX_REPEAT, (1, MAXREPEAT, [(IN, [(RANGE, (97, 122)), (RANGE, (65, 90)), (RANGE, (48, 57)), (LITERAL, 95), (LITERAL, 43), (LITERAL, 45)])]))]))])), (LITERAL, 64), (MAX_REPEAT, (1, MAXREPEAT, [(SUBPATTERN, (2, 0, 0, [(IN, [(RANGE, (97, 122)), (RANGE, (65, 90)), (RANGE, (48, 57))]), (MAX_REPEAT, (0, MAXREPEAT, [(IN, [(RANGE, (97, 122)), (RANGE, (65, 90)), (RANGE, (48, 57)), (LITERAL, 45)])])), (MAX_REPEAT, (0, MAXREPEAT, [(IN, [(RANGE, (97, 122)), (RANGE, (65, 90)), (RANGE, (48, 57))])])), (LITERAL, 46)]))])), (MAX_REPEAT, (2, MAXREPEAT, [(IN, [(RANGE, (97, 122)), (RANGE, (65, 90))])])), (AT, AT_END)]
という表現を得ることができます。
この表現を sre_compile でコンパイルすると実際に正規表現として利用できるようになるようです。
これを上手く使えば正規表現同士を自動的に合成するようなライブラリを作ったりすることができそうです。
複数の正規表現を1つにまとめてくれる「正規表現あせんぶるちゃん」がいつの間にか消えていて、手軽に導入できる代替を探してみたら go で実装されたrassemble-go というプロジェクトを見つけました。
コマンドラインから簡単に導入できるので、自分で使っている環境にインストールしておくと便利です。
以前、正規表現をインタラクティブにデバッグできるRegex101 というオンラインのツールを取り上げましたが、もう一つRegExr というサイトを見つけました。
オンラインで手軽に使えるだけでなく、GitHub でソースコードが配布されている*1ので、用途に合わせて改造したりすることが可能です。
オンラインで正規表現をテストできる「Regex101」というサイトが便利だったのでメモ。
画面はちょっとした IDE のようになっており、画面上部のRegular Expression の部分にテストしたい正規表現を入力し、その下のTest String の部分に検索対象となるテキストを入力すると、マッチした結果がハイライトで表示されるようになっています。正規表現のエンジンはPCRE や JavaScript など、代表的なものがサポートされており、画面左にあるメニューから切り替えができます。
実際に試しながら試行錯誤できるので、プログラムの開発中に役立ちそうです。
Windows のコマンドプロンプトで unix の grep のようなコマンドがあれば良いのに・・・と思って調べてみたら findstr というコマンドを見つけたのでメモ。
いちおう正規表現にも対応しているようなので、外部ツールがインストールできない環境で活躍しそうです。
試しに実行してみたら、こんな感じでした。
複数の正規表現を最適化しながら1つにまとめてくれるRegexp::Assemble は、大きな正規表現を書きたいときにとても便利です。
唯一の不満点は開発時に試行錯誤しながら正規表現を書いていきたいような場合に対話的に実行できないことだったので、上手いやり方がなにかないかと思って探してみたら、PHP 版の Regexp Assemble For PHP を元に JavaScript に移植したバージョンを見つけたのでメモ。
デモサイトである正規表現あせんぶるちゃんを触ってみればわかりますが、実際の正規表現を文章に対して適用したときにどの部分がマッチ対象になるか一目瞭然なので、これでやりたいことはほぼ達成できそうです。埋め込み用の正規表現生成で一度きり正規表現が生成できれば OK という場合も気軽に使えてよさそうですね。
例えばfoobarbazfoobarbaz という文字列に対して/foo|baz/ という正規表現マッチさせると foo と bar が2箇所ずつ合計4箇所マッチするわけですが、マッチした文字列とポジションを一緒に取得をする方法が Ruby にはないようだったので書いてみたら意外と面倒だったので、忘れないようにメモ。
ちょっとムダなことをしてるような気がしなくもないですが、一応できました。
どちらも実行すると{0=>"foo", 6=>"baz", 9=>"foo", 15=>"baz"}という感じになります。
for_1_9_or_later.rb
1.8.5 だと with_object() が使えないので、こんな感じでしょうか。
そこはかとなくダサイ感じですが、今日のところはこの辺で。
for_1_8.rb
以前、Regexp Assemble For PHP は使ったことがありましたがオリジナルのRegexp::Assemble は使ったことがなかったのでちょっと動かしてみました。
このモジュールを使えば、フクロウ本とにらめっこしなくても、複雑な正規表現を効率よく組み立てることができます。
サンプルほとんどそのままですが、UTF-8が通るようにしてあります。
以下、実行例。
[Regexp::Assemble で正規表現を生成する の続きを読む]Regexperという正規表現を可視化するサービス紹介されていて*1便利そうだったのでメモ。
例えばファイル名が .jpg/.jpeg で終わっているかを調べる正規表現(.*\.jpe?g$)を投入してみると、右の図のように可視化されました。
これは簡単すぎるので、もうちょっと複雑な例を投入してみます。ここでは普段はあまり使わないような否定先読みを使ってある00 以外の2桁の数値にマッチする正規表現((?!00)\d{2}) にしてみます。このあたりになると、パット見で何をしているのか分からないかもしれませんが、こういうのを可視化するのには向いてそうです。
あとは大学の講義等で、正規表現の解説なんかにもいいかもしれないですね。
以前、Net::CIDR::LiteというIPアドレスの羅列からCIDR表現を生成するPerlのライブラリを紹介しましたが、今度はその正規表現版とも言うことが出来るphp向けライブラリが公開されていたのでメモ。
これを使うと、複数の単語にマッチする最適化された正規表現を簡単に得ることが出来ます。
改行区切りで単語を入れると、すべての単語にマッチする正規表現を自動的に作成します。
perlにはRegexp::Assembleという正規表現を作成してくれるモジュールがあります。
これを PHPに移植して、 Regexp Assemble For PHPなるモジュールを作って見ました。
例えば、このサイトで使っているコメントのブラックリストの最適化をしてみます。これまで正規表現については単語を単純にOR(|)でつなげていたのですがこのライブラリを使うことでこのように変換することができました。今のところ特に問題はなさそうです。
なかなか面白いライブラリですね。
需要ないかもしれませんが、これを使ってNP_BlackListを書き直したりするとちょっとは動作が速くなったりするかもしれません。