正規表現において、特定の文字やパターンの繰り返しを記述するには、しばしば特殊文字「量指定子」を利用します。
今回、量指定子を使った、文字の繰り返しの正規表現についてまとめました。
目次
繰り返しを表現する「量指定子」
直前の文字やパターンの繰り返し(連続)を表現する特殊文字を「量指定子」と呼びます。「何回繰り返すのか」について、回数か、または範囲を指定する記述となります。
0回以上の繰り返し「*(アスタリスク)」
代表的な量指定子の1つが「*(アスタリスク)」です。これを使って「直前のパターンの0回以上の繰り返し」を表現できます。
// 正規表現
/あぁ*、青春の日々。/
// マッチする文字列の例
あ、青春の日々。
あぁ、青春の日々。
あぁぁぁぁぁぁ、青春の日々。
1回以上の繰り返し「+(プラス)」
アスタリスクによく似た繰り返し表現が「+(プラス)」です。これは、「直前のパターンの1回以上の繰り返し」を意味します。
// 正規表現
/って、おい!/
// マッチする文字列の例
って、おい!
っっっっって、おい!
// マッチしない
て、おい!
パターンのグループの繰り返し
量指定子は、「直前の文字」に作用してしまいますので、複数の文字からなるパターン記述を繰り返しを表現するためには、カッコ「(」「)」を使ってグループ化する必要があります。
// 正規表現
/(オラ)+ーッ!/
// マッチする文字列の例
オラーッ!
オラオラオラオラオラオラーッ!
オラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラーッ!
繰り返し回数の範囲を指定する
上述の量指定子は、1回以上、0回以上、といった表現でしたが、「N回以下」や「N回〜M回の間」という表現も存在します。
// 4回以下繰り返す正規表現
/{,4}/
// 3回以上繰り返す正規表現
/{3,}/
// 10回繰り返す正規表現
/{10}/
繰り返しによる、複数桁の数字のマッチング
これはすなわち、「4桁以下の半角数字」を意味します。下記は半角数字を表すエスケープシーケンス「\d」を使って、これを1〜4回の間繰り返位します。
// 数字が1回以上、4回以下繰り返す正規表現
/\d{1,4}/
// マッチする文字列
1983
0
12
9999
繰り返しによる、余分なスペースのマッチング
2つ以上のスペースをマッチングする例です。(スペースは通常の文字ですので、そのまま記述します。)
// 2回以上繰り返すスペース
/ {2}/
最長の繰り返しをマッチする
さて、便利な量指定子ですが、その利用で注意が必要なのは、「量指定子は通常、最長の繰り返しをマッチする」という仕様です。
例えば「私は・・・・た。」という一文をマッチングしたいとします。
// 正規表現
/私は.+た。/
// 処理対象の文字列
私は昨日、無断欠勤しました。ズル休みでした。
↓ ↓ ↓
// マッチする文字列
私は昨日、無断欠勤しました。ズル休みでした。
このパターンでは、「私の昨日、無断欠勤しました。」をマッチすることができません。量指定子が最長を探してしまうからです。もし最初の一文だけをマッチしたい場合は「最短の繰り返しマッチ」を表す別の表現を用いる必要があります。
最短の繰り返しをマッチする
一般的に、最短マッチは、量指定子の直後に「?」を加えた表現で表されます。すなわち、「+」であれば、「+?」が最短マッチを表す表現となります。下記は「私は無断欠席をしました。」の一文をマッチングします。
// 正規表現
/私は.+?た。/
// 処理対象
私は昨日、無断欠勤しました。ズル休みでした。
↓ ↓ ↓
// マッチする文字列
私は昨日、無断欠勤しました。
最長マッチを「Greedy(欲張りな)なマッチ」、最短マッチを「Non-greedyなマッチ」と、それぞれ呼ぶことがあります。より詳しくは、こちらの記事もご覧ください。
利用可能な量指定子の一覧
量子指定子の一覧です。繰り返し回数によって種々の表現があります。
量指定子(最長一致) | 最短一致 | 意味 |
* | *? | 直前のパターンの0回以上連続 |
+ | +? | 直前のパターンの1回以上連続 |
? | ?? | 直前のパターンの0回か1回の出現 |
{N} | - | 直前のパターンのN回の連続 |
{min,} | {min,}? | 直前のパターンのmin回以上の連続 |
{,max} | {,max}? | 直前のパターンのmax回以下の連続 |
{min,max} | {min,max}? | 直前のパターンのmin回からmax回の連続 |
Vimの量指定子の一覧
繰り返し表現に限ったことではないのですが、Vimのデフォルトのmagicでは、正規表現の記法が異なるため、注意が必要です。こちらもご確認下さい
【参考】繰り返し表現を使った正規表現の例
いくつかの例を吟味すると、より理解を深めれられると思います。簡単な使用例をご紹介します。
1)4桁の数字の表現
基本的な数字の桁数の表現です。文字クラスを利用して文字を限定し、「{n}」によって、繰り返し回数を明示します。
// 例:1224をマッチ [0-9]{4}
2)2000年以上の日付の表現
「日付」の表現記法は、上記の桁数の表現の応用となります。どんな数字をマッチするか、所望の仕様にあわせて正規表現を書き換えください。
// 日付。例:2017-12-21をマッチ 2[0-9]{3}-[01]?[0-9]-[0-3]?[0-9]
3)HTMLの1つのimgタグをマッチする表現
HTML文書では、タグは、文書内(行内)に連続して現れる可能性が大きいでしょう。「.+」などはGreedyに最長一致を検出してしまいますので、最短一致で記述する必要があります。
// <img src="regex.png" alt="正規表現">をマッチ <img .+?>
4)imgタグのsrc属性をマッチする
応用編です。src属性のURL文字列をマッチします。()で囲むことで後方参照できます。<img src=”regex.png” alt=”正規表現”>の「regex.png」をマッチします。最短マッチをすることで、「>」が最も早く出現するよう「.」の繰り返しを制限することができます。
<img .+? src=['"](.+?)['"].+?>
PHPの例です。パターンにデリミタ「/」を使用、また、適切にエスケープしてやる必要があります。とくに、クオーテーションのエスケープは、PHPの文字列表現上の理由で必要です。
$pattern = '/<img.+?src=[\'\"](.*?\.png)[\'\"].*?>/';
$subject = '<nav><a href="regex.html"><img src="regex.png"></a></nav>';
// マッチングを出力
if ( preg_match( $pattern, $subject, $matches ) ) {
var_dump($result);
}
preg_match()の第三引数「$matches」は配列で、初項に全体のパターン全体のマッチ、2番目移行に「()」でグルーピングしたサブパターンのマッチが格納されます。preg_match() 関数自体は、マッチが見つかれば1、見つからなければ0, 何かエラーがあれば、falseを返します。
PHPマニュアルです。
正規表現の繰り返しについては以上です。
正規表現の繰り返しに関する参考情報
繰り返しの記述も「量指定子」「最短マッチ」について理解していればさほど問題なく記述できるでしょう。量指定子含む、正規表現の特殊文字について、詳しくはこちらの記事をご覧ください。
最短一致、最短一致はこちらでも詳しく解説しています。