はうすてんぼぶ

コードかいてて疑問に思ったことや、興味あることをつらつらと暇なときに書く場所、ここはそんな場所

正規表現とか

パーサーを作る上で避けては通れない正規表現

「文法理解しとる?理解しとるかお前!」と聞かれたら「NO!!!」と力強く言えるので、勉強します。

でも、欲しいところを掠めとるだけなら(.*?)だけでいい気はするんだよね!

基本的にパターン文字列を作るときは、PHP正規表現チェッカーを使っています、はい。

文法の種類

使える文法の量は、基本 < POSIX < Perlで増えていく。

POSIX

POSIXは、POSIXクラスという文字区分を定義でき、例えば"[:upper:]"みたいに書くと"[A-Za-z]"と同じ意味を持つ。

便利といえば便利だが、POSIXクラスを使わずに普通に"[A-Z]"と書いた方が分かりやすかったりする。
"[:space:]"は"[ \t\n\r\f\v]"と同じ意味を持つので便利なのかも。あまり色々なスペースに対応しなきゃいけない正規表現を書いたことが無いのでわからないんだけども…。

どちらかといえば、拡張することで、"+"(1回以上の連続する文字)、"?"(0または1回だけの文字)、"|"(OR)の3つのメタ文字が使えるようになることのほうが利点が大きい。

Perl

POSIXもより豊富な文法を持つのがPerl
上で挙げた"[:upper:]"や"[:space:]"などのPOSIXクラスは使えなくなっている。その変わりに、"\d"や"\s"などのクラスが使えるようになっている。

また、".*?"で最短マッチができるようになったのも大きい。
"hogefuga"から、それぞれタグで囲まれた箇所だけを抽出したい場合は、"(.*?)"とするだけで良いから楽チン。

文法

Amazon.co.jp: 改訂 新Linux/UNIX入門: 林 晴比古: 本によると、
以下の6種類が基本的な正規表現記号(メタ文字)として取り上げられているので、それをば。

記号 説明
^ より外なら行先頭。内なら否定
$ 行の末尾
. 任意の一文字
* 直前の文字の0回以上の繰り返し
[] []内の任意の1文字
/ メタ文字のエスケープ

これ以上のメタ文字を使う場合は、だいたいそれはPOSIXかPerlで拡張する必要があるものだと考えて良い、はず。
実際6つだけで事足りることも多いが、Perlに慣れてしまうと、「くっそ!"?"使えないとか詰んでるし!」となる。

文字クラス

"[ ]"は文字クラスとも呼ばれ、"[abc]"とすると「aかbかcのどれか一文字」にマッチする。"[a-z]"という書き方をすると「a,b,c,...,x,y,zのどれか一文字」にマッチする。

"[ ]"の中でしか使えない文法もあるので以下にまとめておく。

記号 説明
- 範囲
^ 先頭に置くと「以外にマッチ」
a-z 小文字の半角英文字
A-Z 大文字の半角英文字
0-9 数字

下3つは定型句みたいな感じの書き方だけども覚えておいて損は無い。
あと、"[A-Za-z]"で半角英数字にマッチになる。

"-"の範囲は文字コードに依存の順番に寄る点に注意。
Unicode対応 文字コード表を見てみるとわかるけれども、UTF-8だとAは41、Zは5A、aは61、zは7Aになっている。
なので、A-zは良くても、a-Zはエラーが返る。
あとZとaの間に"\]^_'"の記号が含まれているので"[A-z]"は『半角英文字』を意味せず、『一部記号と半角英文字』を意味するよくわからん感じになるので、半角英数字をマッチさせたい場合はちゃんと"[a-zA-Z]"としましょう。

実際に使ってみる

対象とするfileには、以下のような文字列が書かれているとする。

% cat file
abcde
a0b1c2d3
bob tom jon
abc123
123abc

それをgrepを用いて行検索をしてみる。

% grep "a" file
abcde
a0b1c2d3
abc123
123abc

% grep "^a" file
abcde
a0b1c2d3
abc123

% grep "$3" file
a0b1c2d3
abc123

% grep "^[a-z]" file
abcde
a0b1c2d3
bob tom jon
abc123

% grep "c[0-9]" file
a0b1c2d3
abc123

% grep "a*d" file
abcde
a0b1c2d3

% grep "a.b" file
a0b1c2d3

文字列置換をする場合には、sedperlを使う。
perlを使うと、Perlの正規表現が使えて便利。

% cat bfile
boom bomb bob

% sed "s/b.*?b/---/g" < bfile
boom bomb bob

% perl -pe 's/b.*?b/---/g' < bfile
---om---ob

()で囲むと後方参照ができる(sedは基本となる正規表現の文法しか使えないので\(\)で囲む必要がある)。
()で囲んだ箇所を\1、\2、…とすることで後方参照できる。

% perl -pe 's/(bo)/\1ooo/g' < bfile
booooom boooomb boooob

おわりに

正規表現に関してまとめ、使ってみた。
行検索する場合のgrep、文字列置換を行いたい場合のsedやperlで正規表現の出番はあるため、しっかりみにつけておく必要があるな、と感じた。

パターン文字列を作るときは、試行錯誤的になりがちなので、正規表現を簡単にチェックしてくれるようなサービスを使えば、捗ると思うよ!

参考資料

Amazon.co.jp: 改訂 新Linux/UNIX入門: 林 晴比古: 本, p130-p135.
UNIXの部屋 コマンド検索: sed, http://x68000.q-e-d.net/~68user/unix/pickup?sed.
Linuxコマンド集 - 【 grep 】 文字列を検索する:ITpro, http://itpro.nikkeibp.co.jp/article/COLUMN/20060227/230786/.