正規表現

正規表現とは

正規表現(Regular Expression)とは、”文字の組み合わせ”を表現する記法の1つで、正規表現が多く用いられる場面は文字列に対してパターン(正規表現)マッチングである。
例えば、毎年リリースされるiPhoneシリーズについて考える。一般的にiPhoneシリーズは「iPhone バージョン」で表されるが「iPhone 4S」や「iPhone X」など規則性が崩れることがある。 ここで、「iPhone NN Pro」(Nは整数)をデータから探すことを考える。このとき、データ入力者によっては”i”と”Phone”の後に半角スペースや全角スペースを入れる場合や”phone”の様に入力する場合もあり表記揺れが発生するかもしれない。 このとき有用になるのが正規表現を用いたパターンマッチングである。

iPhone NN Proを探す

はじめに正規表現を用いてiPhone NN Proのデータを探すためには、表記揺れの許容範囲をする定義する必要がある。今回は、以下のように定義する。
  • “i”と”Phone”の間には「0または1」の「半角スペースまたは全角スペース」を許容する
  • 「“Phone”または”phone”」を許容する
  • “Phone”と”NN”の間には「0または1」の「半角スペースまたは全角スペース」を許容する
  • “Pro Max”シリーズは含まない

メタ文字

正規表現におけるメタ文字とは、特殊な意味や機能をもつ文字のことで、「0または1」等を表現することが出来ます。以下に代表的なメタ文字を記載する。
量化
量化子を置くことによって、直前の表現の出現回数を指定することが出来る。
メタ文字 意味
? 直前の表現が0or1
* 直前の表現が0以上
+ 直前の表現が1以上
{n,m} 直前の表現がn以上、m以下
選言
“|”は選言と解釈され、”|”の直前の表現と直後の表現どちらかをパターンとして指定することができる。
グループ分け
“()”はサブパターンと解釈され、括弧で囲まれたパターンをグループとして指定することが出来る。
位置の指定
位置を表すメタ文字を置くことによって、パターンのマッチングを開始する位置を指定することが出来る。
メタ文字 意味
^ 行頭の位置
$ 行末の位置
定義済みの正規表現
“エスケープシーケンス+文字”で定義済みの正規表現を指定することが出来る。
メタ文字 意味
\t タブ
\r 改行(Carriage Return)
\n 改行(Line Feed)
\d すべての数字
\D すべての数字以外の文字
\s 垂直タブ以外のすべての空白文字
\S すべての非空白文字
\w アルファベット、アンダーバー、数字
\W アルファベット、アンダーバー、数字
任意の1文字にマッチ
“.”は、任意の1文字にマッチするように指定できる
指定した文字のどれか
“[]”は文字クラスと呼ばれ、括弧で囲まれたもののいずれかの一文字をパターンとして指定することが出来る。例えば、[a-zA-Z]はアルファベットのいずれか一文字をパターンとして指定している。

先読みと後読み

イメージ的に位置の指定に近い。先読みや後読みと日本語だと少し混乱するが、英語のlook ahead(先読み)とlook behind(後読み)と考えると分かり易い。以下の4パターン存在する。
肯定の先読み
(?=pattern)で指定し、肯定の先読みの前(ahead)のパターンがマッチした後にpatternがマッチすることを調べる。
否定の先読み
(?!pattern)で指定し、否定の先読みの前(ahead)のパターンがマッチした後にpatternがマッチしないことを調べる。
肯定の後読み
(?<=pattern)で指定し、肯定の後読みの後(behind)のパターンがマッチした後にpatternがマッチすることを調べる。
否定の後読み
(?<!pattern)で指定し、否定の後読みの後(behind)のパターンがマッチした後にpatternがマッチすることを調べる。

iPhone NN Proの正規表現を定義する

“i”と”Phone”の間には「0または1」の「半角スペースまたは全角スペース」を許容する
「0または1」は出現回数なので、”?”で表現できる。「半角スペースまたは全角スペース」は( | )で表現できる。このため、ここでの正規表現は”i( | )?Phone”になる。
「“Phone”または”phone”」を許容する
単純に考えると先ほどと同様に(Phone|phone)になりそうだが、”hone”は共通なので、(P|p)honeで表現できる。このため、前の正規表現と合わせると”i( | )?(P|p)hone”になる。
“Phone”と”NN”の間には「0または1」の「半角スペースまたは全角スペース」を許容する。
ここで新しく必要となるのが、”NN”の2桁の数字。数字が2回出現するので、”[\d]{2,2}”となる。ここまでのを合わせると”i( | )?(P|p)hone( | )?[\d]{2,2}”となる。
“Pro Max”シリーズは含まない
“Pro Max”を考えるとProがマッチした後に、Maxがマッチしなければ良い。このため、否定の先読みを使用すればよい。つまり、”Pro( | )(?!Max)”で表現できる。
以上より、”iPhone NN Pro”の正規表現は”i( | )?(P|p)hone( | )?[\d]{2,2}( | )?Pro( | )?(?!Max)”となる。

C#コード例


    class IPhoneSeries
    {
        private static readonly Regex NNPRo = new Regex("i( | )?(P|p)hone( | )?[\\d]{2,2}( | )?Pro( | )?(?!Max)", RegexOptions.Compiled);

        internal static bool IsNNPro(string iphone)
        {
            var match = NNPRo.Match(iphone);
            return match.Length == iphone.Length;
        }
    }