2009年1月13日火曜日

Emacs Lisp の正規表現

1. バックスラッシュだらけの正規表現

Emacs で .php ファイルを開いたとき、php-mode になるように設定した。

.emacs には、以下の文を追加してある。

(add-to-list 'auto-mode-alist
     '("\\.php[34]?\\'\\|\\.phtml\\'" . php-mode))

正規表現を表した文字列を抜き出す。

\\.php[34]?\\'\\|\\.phtml\\'

パッと見、バックスラッシュが多すぎて、何が書いてあるのか分からない。 (+_+)

 

2. バックスラッシュを使った正規表現の意味

Emacs Lisp プログラミング: 正規表現の探索 によると、

Emacsでは、最初のバックスラッシュはそれに続くバックスラッシュをクオートし、 2番目のバックスラッシュは、それに続く括弧や縦棒が特別な文字であることを表す。

よって、上記の正規表現は、以下のようになる。

\.php[34]?\'\|\.phtml\'

上記には 3 種類のバックスラッシュを使った表現が使われている。

  • \.
  • \|
  • \'

マニュアルを参照。

\.

`.’ は正規表現において、特別な意味を持つ。

33.2.1 正規表現の構文 によると、

`.' (ピリオド)

特別な文字であり、改行以外の任意の1文字に一致する。連結を使って`a.b'のような正規表現を作れる。

文字であるピリオドとして使いたい場合、バックスラッシュでピリオドをエスケープする。

 

\|

正規表現において、`X|Y’ はX, Y の和集合を表す。

33.2.1 正規表現の構文 によると、

多くの場合、任意の文字を伴う`\'はその文字だけに一致します。しかし、いくつか例外があって、 `\'で始まる2文字列が特別な意味を持つ場合があります。

`\|'
選択肢を指定する。 `\|'をあいだに伴った2つの正規表現abは、 abのいずれかに一致する文字列に一致する正規表現となる。

 

\’

33.2.1 正規表現の構文 によると、

つぎの正規表現は空の文字列に一致します。つまりこれらは文字を使用しませんが、これらが一致するかどうか文脈に依存します。

`\''

空の文字列に一致するが、一致対象であるバッファや文字列の末尾に限る。

「バッファ」とは、GNU Emacsマニュアル: 複数のバッファの使い方 によると、

Emacs内で編集しているテキストはバッファ(buffer)と呼ばれるオブジェクトの中に存在します。ファイルを訪問するたびに、ファイルのテキストを保持するバッファを作ります。

では、空文字とは何だろう?

33.2.1 正規表現の構文によると、

`[ ... ]'
`['で始まり`]'で終る文字選択を表す。もっとも単純な場合は、この2つの中括弧のあいだにある文字の1つ1つがこの文字選択に一致する。

したがって、`[ad]'は、`a'1文字か`d'1文字のどちらにも一致する。 `[ad]*'は、`a'と`d'だけから成る(空の文字列を含む)任意の文字列に一致する。このことから、`c[ad]*r'は、 `cr'、`car'、`cdr'、`caddaar'などに一致することがわかる。

`^'
空の文字列に一致する特別な文字であり、一致を取る対象のテキストの行頭のみに一致する。それ以外では、一致に失敗する。したがって、`^foo'は、行頭にある`foo'に一致する。

バッファのかわりに文字列と一致を取るときには、 `^'は文字列の先頭や改行文字`\n'のうしろに一致する。

`$'
`^'と同様だが行末のみに一致する。したがって、`x+$'は、行末にある1文字以上の`x'から成る文字列に一致する。

バッファのかわりに文字列と一致を取るときには、 `$'は文字列の末尾や改行文字`\n'のまえに一致する。

正規表現において、^, $ は、行頭、行末を表す。Emacs のドキュメントによると、 ^, $ は、行頭、行末の「空の文字列」と一致するとの説明。

よって、\’ は、行末を表す $ に類似した表現で、対象がバッファとなっていると見なせば良い。

 

3. re-builder で正規表現の確認

正規表現を確認するためには、re-builder を使う。

48.3.1 正規表現の作成・確認 ― re-builder によると、

簡単な検索ならともかく,複雑な正規表現となると,これで本当にうまく検索できるのか不安になりませんか?

そんな正規表現を実際に試すことができるものが, re-builder です.

標準で付属してますので,M-x re-builderとするだけで使うことができます.

re-builder の入力により、“\\’” でバッファの末尾に一致。 “\\`” でバッファの先頭に一致。

“\^” が行における先頭を表し、”\$” が行末を表すのと同じ。

 

4. 正規表現をリテラルに戻す

Emacs には、次の関数が定義されている。

GNU Emacs Lisp Reference Manual - 正規表現のマッチングを行なう関数

Function: regexp-quote string

この関数は、 string に正確にマッチし他のものにマッチしない正規表現のストリングを返します。

先ほどの正規表現に対して、この関数を適用してみる。

(message (regexp-quote "\.php[34]?\'\|\.phtml\'"))
"\\.php\\[34]\\?'|\\.phtml'"

元の表現には、戻らなかった。

Emacs Lisp によると、

もし、バックスラッシュが多すぎてよく分らないのなら、正規表現を評価した後の形について考えればよい。文字列を評価する簡単な方法としては、 *scratch* バッファに insert することが挙げられる。

(insert "\\\\\\*")
→ \\\*

"\\\\\\*" という謎の正規表現も、'\\\*' となる。こればバックスラッシュの後にアスタリスクが続く文字列に合致する。バックスラッシュもアスタリスクもメタ文字なので、バックスラッシュが前についてzいるだけのことだ。

初心者は insert による方法を思い付かず、逆にリテラルを正規表現に直そうとする傾向にあるようだ。そして、regexp-quote という関数を発見してぬか喜びをする。この関数の役割は、リテラルを正規表現へ直すことではないから、忘れる方がいい。

元の表現に insert 関数を適用してみる。

(insert "\\.php[34]?\\'\\|\\.phtml\\'")

結果は、

\.php[34]?\'\|\.phtml\'

 

5. その他

正規表現については特によく忘れる… (+_+)

Python における正規表現もややこしかった。

 

関連記事

関連サイト