正規表現: 先読み・後読み言明 (look-around assertions)

グローバルソリューション事業部 情報システムグループの平田です。

拡張された正規表現のうち「先読み・後読み言明」について簡単に紹介したいと思います。

正規表現とその拡張

元々形式言語理論で使われていた正規表現 (regular expression) は、プログラミングの分野で「文字列のパターンマッチ言語」として応用されています。Unixのツール類では初期から正規表現が実装されていましたが、その後拡張正規表現が導入され、さらにRubyやPerlなどの新しいソフトウェアではさらに拡張された独自の正規表現を利用することが出来ます。

今回はRubyを例にとって拡張された正規表現の「先読み・後読み言明」機能を紹介しますが、Perl等でも同様に利用することが出来ます。

先読み・後読み言明の利用

次のような「ひのきのぼう値段表」の文字列を考えます。

ある日、書かれている値段から一律10%の値上げをすることになったため、値上げのため文字列中の「1024円」「10ドル」のように書かれた部分を書き換える作業が必要になりました。その際、特に何も考えず書くと次のようなコードになるでしょう。

これでは数字では無い部分 (「円」「ドル」) までキャプチャされてしまいますので、最後 $2 の部分を連結する必要があります。できれば数字部分だけをキャプチャして、シンプルに処理したいところです。

このような場合、先読み・後読み言明 (look-around assertions) を利用することで 「この前方 (後方) に~が来る (来ない)」を指定しつつ、その部分のキャプチャを避ける (マッチ範囲に含まれることを防ぐ) ことが可能になります。

先読み・後読み言明 一覧
種類 説明 記述 備考
肯定先読み言明 前方 (右側) に~が来る (?=パターン)
否定先読み言明 前方 (右側) に~が来ない (?!パターン)
肯定後読み言明 後方 (左側) に~が来る (?<=パターン) 固定長のパターンのみ
否定後読み言明 後方 (左側) に~が来ない (?<!パターン) 固定長のパターンのみ

【注意】

  • 「先読み」「後読み」と日本語で書くと分かりにくいのですが、文字列は右側に伸びていくので右側が「先」です。(前者は “look-ahead”、後者は “look-behind”)
  • 先読み・後読み言明自体は特定のパターンが来ることを「言明」するだけで、マッチ幅は常に0文字となります。

肯定先読み言明を利用してマッチの様子を確認してみましょう。

肯定先読み言明を利用した後者の例では、「円」「ドル」が \d+ の右側に来ることを指定しつつ、「円」「ドル」へのマッチを避けることができました。 これを利用して最初のコードを書き換えると、次のようになります。

応用例

2つのマッチ条件を1つの正規表現にまとめる

ある文字列が2つの正規表現にマッチするか確認することを考えます。

変数 name には “名前 名字” または “名字, 名前” のいずれかの形式で名前が入っている場合を考えます。ここで、次のようなコードで “Fate” と “Testarossa” の両方が含まれることを確認することが出来ます。

これを1つの正規表現にまとめるようとすると

となりますが、同じ文字列が2回出てくるのはあまりきれいではありません。

これは次のようにして「先頭の空文字列」の右に「任意の文字列、その右に “Fate”」(ただし、マッチ位置は移動しない)、さらに「先頭の空文字列」の右に「任意の文字列、その右に “Testarossa”」という正規表現で表すことができます。

マッチの図解

以下にマッチする範囲を示します。 (?=.*Testarossa) の前にある空文字列のマッチ位置に注意してください。

name = “Fate Testarossa” の場合:

name = “Testarossa, Fate” の場合:

「直後に○○ (ただし××ではないもの) が来る」を表現する

次の配列から /von [A-Z]/ が含まれるものを抽出 (ただし、”von Neumann” のみを除外する) 場合を考えます。

この場合、「”von ” の後に [A-Z] が来る」と「”von ” の後に”Neumann” が来ない」の両方を満足させればよいので、次の正規表現を利用することが出来ます。

この条件で select してみましょう。

無事抽出することができました。

まとめ

この先読み・後読み言明を上手く利用することでコードをシンプルに書くことが出来ます。また、ユーザ入力を正規表現として利用する際に「and条件」「not条件」などを実現するのは面倒ですが、拡張された正規表現を利用することで制限付きながら似た機能を実現することが出来ます。

拡張された正規表現は必ずしも必要な機能ではありませんが、使い方によっては非常に便利なものです。機会があれば使ってみてはいかがでしょうか。

Share on LinkedIn
LINEで送る
Pocket

平田

平田

本業はインフラエンジニアだけど、最近はプログラミングやっています。