正規表現の基本パーツ:これだけ分かれば読み書きできる

正規表現は一見暗号のようですが、実際には少数の組み合わせパーツの繰り返しだと気づくと急に読めるようになります。

アンカー(^ と $)は位置を固定する

^は文字列(複数行モードでは行)の先頭、$は末尾にマッチし、これらを使わないとパターンが文字列内のどこにでもマッチしてしまい、意図しない部分一致の原因になる。

文字クラスは候補の中の1文字にマッチ

[abc]はa・b・cのいずれか1文字にマッチし、[a-z]のような範囲指定や\d(数字)・\w(単語文字)・\s(空白)のようなショートハンドがよく使われる。

量指定子は繰り返し回数を制御する

*は0回以上、+は1回以上、?は0回か1回、{n,m}は具体的な回数の範囲を表し、いずれも直前の要素にだけ適用される。

丸括弧はグループ化とキャプチャを兼ねる

(パターン)はサブパターンをまとめて量指定子を一括適用できるようにすると同時に、マッチした文字列を後から参照できるように保存する。キャプチャが不要な場合は(?:パターン)を使う。

貪欲マッチはデフォルトでできるだけ長く一致しようとする

.*のような量指定子はデフォルトで最長一致を試み、後ろに?を付けた.*?にすると最短一致(非貪欲)になり、文字列中に同じパターンが複数回現れる場合に結果が大きく変わる。

同じ正規表現でも言語によって挙動が違う理由

PCRE、JavaScriptのRegExp、Pythonのreなど、エンジンごとに基本構文は共通していても、\dがASCII数字のみにマッチするかどうかや後読み(lookbehind)への対応状況など細部が異なるため、あるエンジンで動作確認したパターンが別の言語でも同じように動くとは限らない。

正規表現はパターンマッチが得意で、構文解析は苦手

HTMLやJSONのような本格的に構造化されたフォーマットを正規表現だけで扱おうとすると、複雑になるほど壊れやすく保守しにくくなる。メールアドレスの形式チェックや文字列から数字だけを抜き出すような、行単位のフラットな検証・抽出にこそ向いている。

よくある質問

想定より広い範囲にマッチしてしまうのはなぜですか?

多くの場合、貪欲な量指定子が想定以上に長く一致しているのが原因。非貪欲な書き方(*?や+?)に変えるか、広いワイルドカードの代わりに文字クラスを絞り込むとよい。

キャプチャグループと非キャプチャグループの違いは何ですか?

キャプチャグループ(パターン)はマッチした文字列を後で参照(置換文字列などで利用)できるように保存するのに対し、非キャプチャグループ(?:パターン)はグループ化と量指定子の適用範囲だけに影響し、何も保存しない。