正規表現を使って「最短一致」でマッチングを取ります。
通常、正規表現では、「*」などの量指定子を利用してマッチを検出した時、「最長一致」を検出しますが、特定の記述方法によって、パターンの「最短一致」を表現することができます。
目次
最短一致でマッチさせる正規表現
最長一致と最短一致
正規表現での「*」「+」「?」「{n}」などの量指定子(Quantifier)は、文字、文字クラスやサブパターン、またはメタ文字などの後ろにくっつけることで、「文字の繰り返し」を表現できるのはよく知られています。
このとき、量指定子はデフォルトでは「Greedy(欲張り)なマッチ」を行う仕様となっており、可能な限り長い文字列のマッチを探してしまいます。
そのような量指定子の直後に「?」を置くことで、逆に「Non-greedy(最短)なマッチ」を表現でき、可能な限り短い文字列のマッチ、すなわち最短マッチを検出するようになります。
// 「PATTERN」の最長一致
/PATTERN*/
// 「PATTERN」の最短一致
/PATTERN*?/
正規表現の量指定子
量指定子には下記のようなものがあります。
量指定子(最長一致) | 最短一致 | 意味 |
* | *? | 直前のパターンの0回以上連続 |
+ | +? | 直前のパターンの1回以上連続 |
? | ?? | 直前のパターンの0回か1回の出現 |
{N} | - | 直前のパターンのN回の連続 |
{min,} | {min,}? | 直前のパターンのmin回以上の連続 |
{,max} | {,max}? | 直前のパターンのmax回以下の連続 |
{min,max} | {min,max}? | 直前のパターンのmin回からmax回の連続 |
※Vimのmagicを利用した正規表現は、記述方法が異なりますので、ご注意下さい。こちら「 正規表現:特殊文字(メタ文字)の一覧 」をご覧ください。
最短マッチを見つける正規表現
さて、具体例を見ていきましょう。例えば、から<a></a>で囲まれたアンカータグを「1つだけ」取り出す事を考えます
<nav>
<a href="/about">About</a> | <a href="/access">Access</a> | <a href="/contact">Contact</a>
</nav>
改行以外のすべての文字を表す「.」を利用して、「<a . *</a>」と言った正規表現が思いつきます。ですが、これだと「最長一致」を探してしまい、上記の例だと、3つのアンカータグをひと続きでマッチしてしまいます。
// アンカータグを検出?(デリミタに#を利用)
#<a.*>(.*)</a>#
// マッチする文字列
<a href="/about">About</a> | <a href="/access">Access</a> |<a href="/contact">Contact</a>
ここで、上記の Non-greedyな最短マッチングを利用します。
「?」記号を利用した「.*?」で、最短一致の繰り返しをマッチさせれば、個々のアンカー要素を取り出すことができそうです。
// アンカータグを最短一致で検出
#<a.*?>(.*?)</a>#
// マッチする文字列
<a href="/about">About</a>
最短一致の実際の表現例
最短のマッチングの実際の使用について、下記にいくつかの例を見てみます。
Javascript
RegExpクラスの「testメソッド(booleanを返す)」「execメソッド(nullか、もしくはマッチ文字列の配列を返す)」を使えます。
var re = new RegExp("<a.+?</a>"); var result = re.exec('<a href="/x">xx</a><a href="/y">yy</a>') if (result === null) { alert('見つかりませんでした・・。'); } else { // 最初のアンカータグを取り出せる alert('最初のアンカーは、' + result[0] + 'です!'); }
なお、Javasciptでは、正規表現リテラルを使って、「var re = /<a.*?<\/a>/;」とも表現できますが、上記のようにnewを利用するとデリミタを書く必要がないので、HTMLタグにおけるスラッシュをエスケープしなくて済みます。(Javascriptでは、デリミタに「/」以外が使えないのです)お好みでどうぞ。
PHP
preg_xxx系の関数は色々あります。(PHPマニュアル:preg_match)下記は最も基本的な、マッチング結果をbooleanで返す関数の例です。
if ( preg_match( '#(<a.*+</a>)#', '<a href="/x">xx</a><a href="/y">yy</a>', $m ) ) { echo "ありました。最初のアンカーは、" . $m[1] . "です。"; }
引数の使い方に注意します。3番目の変数に、後方参照用の配列を渡します。見つかれば、その配列の、N番目の要素に、マッチのN番目が入ります。
また、通常は、デリミタを「/ (スラッシュ)」で記述しますが、代わりに「#」を利用することで、スラッシュをエスケープする必要がなくなります。これは便利ですよね。デリミタは英文字やバックスラッシュ、スペース以外ならなんでもいいようです。
筆者は「#」をよく使います。(お好みでどうぞ。)
Vim
Vim の magic による正規表現はクセがあるので注意が必要です。
magicにおける0回以上の繰り返しの最短マッチは「\{-}」です。
// 例1:カウントを返す %s#<a.\{-}</a>#&#gn
ちなみに、末尾に「n」をつけることで、文字列置換を実際には行いません。マッチのカウントをVIM上に表示だけするためにこれ付けています。
下の例は、すこし複雑に見えますが、後方参照により、アンカータグだけを取り除きます。
// 例2:アンカータグを取り除く %s#<a.\{-}>\(.\{-}\)</a>#\1#g
正規表現に関する参考リンク
さて、うまく最短マッチの正規表現がかけたでしょうか?
現場からは以上です。