正規表現入門
この記事は、TUT Advent Calendar 2016 - Adventar の13日目の記事です。
エクストリーム正規表現入門はこちら
正規表現とは?
正規表現は、文字列のパターンを表す文字列である。これによってgrepなどで検索や置換をすることができる。
プログラミング言語にかかわらずエディタでも同様に使えるため、プログラムを書かない人も、覚えると便利だと思う。
今回は環境構築等を省くため、ブラウザ上で動く以下のツールを紹介する。
PHP正規表現チェッカー
ただ、正規表現は言語や環境で大きく変わるものではないため、リンク切れやうまく動かない場合は適当なツールで試すことができるはずである。
正規表現を学ぶ前に
まず大事なことは、最初から正規表現で全てを表現しようとしないことである。
入力値の法則を完璧に見極めないうちから細かな正規表現を書くと、人的リソースや計算リソースを浪費するばかりでなく、正常な動作が損なわれる原因にもなる。
最終更新日時: 2016/11/29 23:0:0
投稿日時: 2016/08/10 11:45:14
最終更新日時: 2016/08/10 11:45:14
こんなテキストから投稿日時を抜き出すとしよう。今回説明することをマスターすれば、下のような正規表現は簡単に書くことができるようになる。この記事を読み終わったら戻ってきて解読してみてほしい。
しかしこれは4月31日や平年の2月29日も含めてしまうし、もっと致命的な例では閏秒を含まない。日付チェックなどは組み込み関数やライブラリに任せてしまって、正規表現ではそれっぽい箇所を抜き出すだけに留めるべきである。以下で十分だ。
長々と正規表現を書いていると、気持ちよくなってきて更に長い正規表現を書いてしまう人がたまに居るが、あくまで奥の手か孫の手くらいにしておいて、複雑な正規表現をプログラムでゴリゴリ回すのはやめるべきである。メンテナンス性が悪い上に重くなる可能性が高い。
まずは単なる文字列探索
ここからhogeを探してみよう。
対象文字列にfoobarbazquxquuxhogepiyofugahogerahamspam、
パターン文字列にhogeと入力してみると、
hogeに一致(以後マッチ)する箇所に色がつく。ただの文字列検索のようだが、これも立派な正規表現である。
文字種と文字クラス
- .
- 改行を除くあらゆる文字にマッチする。
- []
- []で囲んだ中の1文字。
[flmstw]axはfax, lax, max, sax, tax, waxのいずれにもマッチする。 - 文字と文字の間にハイフンを入れるとその間の各文字のいずれにもマッチするようになる。
あ[いさ-そり]はあい, あさ, あし, あす, あせ, あそ, ありのいずれにもマッチする。文字コードによってはあざ, あじ, あず, あぜにもマッチする。 - [^ac-f]のように内に^を付けると、それらと改行を除くあらゆる文字にマッチする。
-
[^ac-f]は!, ", #, ..., 0, 1, 2, ..., 9, ..., Z, b, g, h, i, ..., z, あ, い, う, ...など、様々な文字にマッチする。ここには一部の制御文字も含まれる。
- \n
- 改行
- \t
- 水平タブ
- \s
- [ \t\n\r\f]
- \S
- [^ \t\n\r\f]
- \d
- [0-9]
- \w
- [0-9A-Z_a-z]
- \
- [, ], \, ^や、あとで紹介する(, )など、特殊な役割を果たす文字にマッチさせたい場合は、その文字の前に\を付ける(エスケープする)。
たとえば、 からbaz["huga"]を正規表現を使って検索したい場合、baz\["huga"\]とエスケープする必要がある。\にマッチするには\\とする。 - ^
- の外に^をおいた場合、検索対象の先頭(状況によっては行頭)にマッチする。
foo1foo2foo3foo4の中で^foo\dにマッチするのは、foo1のみである。
- $
- 検索対象の先頭(状況によっては行頭)にマッチする。
foo1foo2foo3foo4の中でfoo\d$にマッチするのは、foo4のみである。
量指定子
- ?
- 0回または1回の繰り返し。
meteo?rはmeterとmeteorのどちらにもマッチする。
- *
- 0回以上の繰り返し。
go*gleはggle, gogle, google, gooogleやgoooooooooooogleなどにマッチする。
- +
- 1回以上の繰り返し。
go+gleはgogle, google, gooogleやgoooooooooooogleなどにマッチする。ggleにはマッチしない。
文字列から数字列を取り出すには\d+とすればよい。 - {}
- {num}とすると、num回の繰り返しを意味する。
は
IE{3}とひとしい。
- {min,max}とすると、意味するmin回以上max回以下の繰り返しを意味する。
つまりa?はa{0,1}とひとしい。 - {min,}とすると、min回以上の繰り返しを意味する。
つまりa*はa{0,}とひとしい。
また、a+はa{1,}とひとしい。
例題
6桁の十六進カラーコードにマッチするパターンを記せ(14文字以内)。ただし大文字小文字は区別しないものとする。
つまり以下のようにマッチすればよい。
PHP正規表現チェッカーを用いる場合は、マルチラインモードをオフにするとよい。
#FFFFFF
#ff0000ABC
a#000000
#77777
#GGGGGG
000000
余力があれば、13文字での表現も考えてみてほしい。
^#[\dA-F]{6}$
グループ化
(?:pattern)のように、(?:)で囲むとそのグループをひとかたまりとすることができる。
は
にマッチする。
OR
_(?:foo|bar)!のように、|で区切ると_foo!にも_bar!にもマッチする。|は(?:)の外でも使用できるが、可読性の向上のためにつけることを推奨する。
例題
IPv4アドレスを示す行にマッチする正規表現を記せ(目標100文字以内、どうしても思い浮かばなければ175文字以内)。
つまり、以下のように0-255の数4つをドット区切りした文字列にマッチすればよい。
PHP正規表現チェッカーを用いる場合は、マルチラインモードをオフにするとよい。
198.51.100.35
203.0.113.59
192.0.2.256
192.000.002.012
123.456.789.012
IPv4アドレスは、先頭に0があるバイトは八進数として扱われる(192.000.002.012は192.0.2.10と解釈される)。余力があれば、これらの変則的な表記に対応した表現も考えてみてほしい。
グループのネストを用いる。ドットのエスケープに気をつけてほしい。
後方参照
(pattern)のように、()で囲むとそのグループをひとかたまりとした上で、後から番号で参照することができる。
は
にマッチする。
にはマッチしない。つまり、\1には1234、\2にはABCDが入っている。
PHP正規表現チェッカーを用いる場合は、マッチグループを表示するとこれらを確認することができる。
また、後方参照は置換に用いることができる。
円は対ユーロで下落に転じた。10時時点では1ユーロ=122円38銭〜41銭と同3銭の円安・ユーロ高水準で推移している。対ドルでの円買いの一服がユーロにも波及した。
に対して、パターン文字列を
置換え文字列を
とすると、
円は対ユーロで下落に転じた。10時時点では1ユーロあたり122円と同3銭の円安・ユーロ高水準で推移している。対ドルでの円買いの一服がユーロにも波及した。
と置き換えることができる。
環境によっては$1, $2, ...を用いる場合もある。
先読み・後読み
後読み
(?<=foo)のように(?<=)で囲むと、遡ってマッチさせることができる。
bar
foobar
に対して
は
bar
foobar
といったようにマッチする。
先読み
(?=foo)のように(?=)で囲むと、先行してマッチさせることができる。
bar
foobar
に対して
は
bar
foobar
といったようにマッチする。
後読み否定
後読みの=の代わりに、!とすると先読みグループ内にマッチしないものにマッチする。つまり、先読みグループに続かないものにマッチする。
bar
foobar
barbar
に対して
は
bar
foobar
bazbar
といったようにマッチする。
先読み否定
先読みの=の代わりに、!とすると後読みグループ内にマッチしないものにマッチする。つまり、後読みグループが続かないものにマッチする。
ちょうど6桁の数を表す正規表現は
である。
結び
正規表現の様々な構文を紹介したが、これらを一度に覚えるのは難しい。日常の作業で長い文字列を扱うことがあれば、その都度調べながら正規表現の恩恵を享受し、覚えていってほしい。