正規表現の逆方向検索

時間がぽっかり空いたのでネットを巡回していました。

そしたら拙作SkRegExpへダメ出ししているブログが!

読んでみると、SkRegExpは「逆方向検索に対応していないからダメだ」と書かれていました。

確かに、SkRegExpは逆方向検索に対応していません。対応しようと考えたこともありません。

なぜなら、正規表現の逆方向検索ってどうすればいいのかわからないからです。

正規表現パターンを後ろから評価して検索すればいいんでしょうか?

ただし、これでは順方向で検索したときと結果が変わります。

たとえば、a*b と言う正規表現で aaabc と言う文字列を検索した場合。

順方向ではaaab がマッチします。

では逆方向から検索すると?

a*b を後ろから評価すると b*a です。

この場合、マッチするのは ab です。

順方向と逆方向でマッチ結果が異なります。

これでいいのでしょうか?

たぶん、違うでしょう。順方向でも逆方向でも同じ結果が欲しいはずです。

となると、考えられる方法は一つ。

文字列の末尾から一文字ずつ正規表現を適用していき、もっとも長くマッチした文字列でマッチ成功とすることです。

正規表現パターン a*b を文字列 aaabc に逆方向からマッチさせてみます。

  1. 5文字目 c の位置ではマッチしない
  2. 4文字目 b の位置では b にマッチする。
    ただし、最長マッチではないかもしれない。マッチした情報を保存し、3文字目からマッチを再試行。
  3. 3文字目 a の位置では ab にマッチする。
    ただし、最長マッチではないかもしれない。マッチした情報を保存し、2文字目からマッチを再試行。
  4. 2文字目 a の位置では aab にマッチする。
    ただし、最長マッチではないかもしれない。マッチした情報を保存し、1文字目からマッチを再試行。
  5.  1文字目 a の位置では aaab にマッチする
    これ以上文字列がない。したがってこのマッチが最長マッチ。aaab をマッチ結果として返す。

このような短い文字列でも、重い処理である正規表現マッチを5回繰り返す必要があります。

こんなことをするくらいなら、順方向に全マッチさせた方がはるかに速いです。最適化された正規表現ならなおさらです。

というわけで、SkRegExp が逆方向検索をサポートすることはありませんのでご了承下さい。

SkRegExpの最新バージョンはコチラからダウンロードできます。

 

Delphi ソースからコメントを削除する正規表現

拙作の SkRegExp に興味を示すガイジンさんがたまにいます。でも、ソースを開いてびっくりするようです。

ファイルが壊れているとか、ウィルスに感染しているとかメールをくれる人がいるくらいですからね。

それって日本語なんですけどね。NO VIRUS, It’s Japanese!!

てなわけで、SkRegExp の世界進出にはソースから日本語を削除する必要があると確信しました。

それならコメントを削除すればいいだろうと正規表現を書いてみました。もちろん、使うエンジンは SkRegExp です。

sStrings := '(''[^'']*?'')';
sCompiler := '({$.*?})';
sLineComment := '(//.*)$';
sBraceComment := '({[^}]*?})';
sPascalComment := '((*[^*]**+([^(*][^*]**+)*))';
LRegExp.MultiLine := True;
LRegExp.Expression := sStrings + '|' + sCompiler + '|' +
sLineComment + '|' + sBraceComment + '|' + sPascalComment;
Memo1.Lines.Text := LRegExp.Replace(Source, '$1$2');

考え方は以下の通り。

文字列(の中のコメントと同じ文字) sStrings と(コメントと似ている)コンパイラ指令 sCompiler はコメントではないのでマッチさせてそのまま残します。

あとは Delphi の3つのコメントにマッチする sLineComment、sBraceComment、sPascalComment をそれぞれ選択で一つにします。

この正規表現を使って ‘$1$2’ で置換すると、文字列とコンパイラ指令はそのまま残りますが、それ以外のコメントは消えてなくなります。

実は、このままだとちょっと遅いんですが、動作はわかりやすいと思います。速くするのはぜひそれぞれお考えください。

あ、遅いのは SkRegExp の Replace メソッドの手抜きも原因ですけどね。

正規表現の基本要素

せっかくの正規表現ライブラリです。SkRegExp も使ってもらわなければ私も面白くないので、正規表現の入門なんて書いてみます。

ぜひ、SkRegExp をインストールし、試しながら読んでください。

正規表現をカンタンに試せるプログラムも作りました。ダウンロードして、実際に動かしながら SkRegExp と正規表現を楽しんでください。

正規表現は文字列ではなく、文字パターンにマッチします。

文字パターンにマッチさせるため、一部の文字はパターンを表現するために使われます。これをメタ文字と言います。

基本的なメタ文字を以下に示します。

|

縦棒は選択肢を表します。
たとえば、”です|である”という正規表現は、”です”と”である”にマッチします。

?

クエスチョンマークは、直前の正規表現がないか、あるいは1個あるときにマッチします。
たとえば、”新人類?”という正規表現は、”新人”と”新人類”にマッチします。
もう少し詳しく説明すると、”新人類?”で、?の直前の正規表現は”類”です。つまり、”類”はあってもなくてもいいのです。
したがって、この正規表現は、”新人”と言う文字の後に”類”が付いていてもいなくてもマッチします。

*

アスタリスクは、直前の正規表現がゼロ個以上あるときにマッチします。
たとえば、”あい*ん”という正規表現は、”あん”、”あいん”、”あいいん”、”あいいいん”などにマッチします。
“あん”にもマッチすることがポイントです。アスタリスクは、直前の正規表現がゼロ個以上あるときにマッチします。したがって、”あん”のように、間に「い」がなくてもマッチします。

()

括弧は、正規表現の優先順位を変更します。
たとえば、”新人類?”という正規表現を、”新(人類)?”に変更すると、”新”と”新人類”にマッチします。クエスチョンマークの対象が括弧でくくられた”人類”に変わったためマッチする文字列も変わったのです。

以上が正規表現の基本構文です。これだけでは、大した使い道がなさそうに思いませんか?

正直、私も最初はそう思いました。

たとえば、日本語で何個も同じ文字が続くなんてありません。だから、アスタリスクなんか何に使うんだよと思いました。

ところが、次のような正規表現で威力を発揮します。
(.*)

まだ説明してないメタ文字がありますが、この正規表現は括弧でくくられた文字列を検索できます。

ぜひ、SkRegExpで入力して試してみてください。

詳しくは次回説明します。

正規表現は何に使うのか?

開発が一段落したとは言え、ブログを放置しておくのもあれなので正規表現について書いてみる。

正規表現が役に立つのはこんなとき。

HTMLテキストの中のメールアドレスを探して強調表示に変えたい。

テキストの量が少なければ手作業でできる。でも、大量になったらやってられない。

こんなときが正規表現の出番。

たとえば、次のような正規表現を使えばメールアドレスっぽいところを見つけることができる。

([w-.]+)@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.)|(([w-]+.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(]?)

メールアドレスっぽいといったのは、メールアドレスの書式はものすごく複雑で、これで完全とは行かないのだ。

しかし、手作業でやるよりは時間を節約できる。

特定の文字ではなく、特定のパターンのテキストを探すとき、正規表現が最適だ。というか正規表現以外に方法はない。

実際には、特定の文字を探すより特定のパターンを探すことの方が作業としては圧倒的に多い。正規表現を知らなければ大変な時間のロスをすることになる。

正直、正規表現は難しい。でも、覚えたら下手な資格よりもはるかに役に立つ。就職にも有利かもしれない(知らないけど)。

いつになるかわからないが、次回は正規表現の入門を書いてみたいと思う。