Main Content

動的正規表現

はじめに

動的表現では、regexp で一致させるパターンを入力テキストに基づいて作成できます。これにより、解析対象のテキスト内のさまざまな入力パターンに、より厳密に一致させることができます。動的表現は、関数 regexprep で使用される置換語句でも使用できます。これによって、解析される入力に置換テキストを適応できるようになります。

これらのコマンドの引数 match_expr または replace_expr には、任意の数の動的表現を含めることができます。

regexp(text, match_expr)
regexpi(text, match_expr)
regexprep(text, match_expr, replace_expr)

動的表現の例として、次の regexprep コマンドでは、用語 internationalization が省略形の i18n に正確に置き換えられます。ただし、これを globalization のような別の語で使用するには、別の置換式を使用しなければなりません。

match_expr = '(^\w)(\w*)(\w$)';

replace_expr1 = '$118$3';
regexprep('internationalization', match_expr, replace_expr1)
ans =

    'i18n'
replace_expr2 = '$111$3';
regexprep('globalization', match_expr, replace_expr2)
ans =

    'g11n'

動的表現 ${num2str(length($2))} を使用すると、入力テキストに基づいて置換式を指定でき、毎回式を変更する必要がなくなります。この例では、動的置換構文 ${cmd} を使用します。

match_expr = '(^\w)(\w*)(\w$)';
replace_expr = '$1${num2str(length($2))}$3';

regexprep('internationalization', match_expr, replace_expr)
ans =

    'i18n'
regexprep('globalization', match_expr, replace_expr)
ans =

    'g11n'

解析する際に、動的表現は完全で有効な正規表現に対応していなければなりません。さらに、バックスラッシュ エスケープ文字 (\) を使用する動的一致表現は、2 つのバックスラッシュが必要です。1 つは式の初期解析に使用し、もう 1 つは完全な一致に使用します。動的表現を囲む小かっこでは、キャプチャするグループを "作成しません"。

次の節で説明するように、動的表現には一致表現で使用できる形式が 3 つ、置換式に使用できる形式が 1 つあります。

動的一致表現 — (??expr)

(??expr) 演算子では表現 expr が解析され、結果が一致表現に挿入されます。次に、MATLAB® では修正された一致表現が評価されます。

次に、この演算子で使用できる表現の例を示します。

chr = {'5XXXXX', '8XXXXXXXX', '1X'};
regexp(chr, '^(\d+)(??X{$1})$', 'match', 'once');

このコマンドの目的は、入力 cell 配列に格納された各文字ベクトルで連続する文字 X を見つけることです。ただし、X の数は各文字ベクトルによって変わります。カウントが変化しない場合は、式 X{n} を使用して、これらの文字の n 個を一致させたいことを示します。ただし、一定値 n としてしまうと、この例ではうまく機能しません。

ここで使用する解決方法では、先頭のカウント数 (たとえば、cell 配列の最初の文字ベクトルでは 5) をトークンにキャプチャして、動的表現でこのカウントを使用しています。この例の動的表現は、(??X{$1}) です。ここで、$1 はトークン \d+ によってキャプチャされた値です。演算子 {$1} ではこのトークン値の量指定子が作成されます。表現が動的であるので、同じパターンを cell 配列の入力ベクトルの 3 つすべてに使用できます。最初の入力文字ベクトルで、regexp によって 5 つの X 文字が検索されます。2 番目の入力文字ベクトルでは 8 つが検索され、3 番目の入力文字ベクトルでは 1 つだけ検索されます。

regexp(chr, '^(\d+)(??X{$1})$', 'match', 'once')
ans =

  1×3 cell array

    {'5XXXXX'}    {'8XXXXXXXX'}    {'1X'}

一致表現を変更するコマンド — (??@cmd)

MATLAB では、(??@cmd) 演算子を使用して、一致表現に MATLAB コマンドの結果が含められます。このコマンドでは、一致表現内で使用できる語句を返さなければなりません。

たとえば、動的表現 (??@flilplr($1)) を使用して、長い文字ベクトルの中にある「Never Odd or Even」という回文を検索します。

まず、入力文字列を作成します。すべての文字が小文字になるようにして、すべての語以外の文字を削除します。

chr = lower(...
  'Find the palindrome Never Odd or Even in this string');

chr = regexprep(chr, '\W*', '')
chr =

    'findthepalindromeneveroddoreveninthisstring'

動的表現を使用して、文字ベクトル内の回文を特定します。

palindrome = regexp(chr, '(.{3,}).?(??@fliplr($1))', 'match')
palindrome =

  1×1 cell array

    {'neveroddoreven'}

動的表現では文字ベクトルを構成する文字の順序が逆にされ、順序が逆になったベクトルでできるだけ多くの一致を試みます。$1 の値はトークン (.{3,}) の値に依存しているので、これには動的表現が必要です。

MATLAB の動的表現では、現在アクティブなワークスペースにアクセスできます。つまり、ワークスペースの変数を変更するだけで、動的表現で使用される任意の関数や変数を変更できます。上の例の最後のコマンドを繰り返しますが、今度はベース ワークスペースに保存された関数ハンドルを使用して、式内で呼び出される関数を定義します。

fun = @fliplr;

palindrome = regexp(chr, '(.{3,}).?(??@fun($1))', 'match')
palindrome =

  1×1 cell array

    {'neveroddoreven'}

関数として機能するコマンド — (?@cmd)

(?@cmd) 演算子では、一致表現全体を解析する際に、regexp または regexprep で実行する、MATLAB コマンドが指定されます。MATLAB の他の動的表現とは異なり、この演算子が使用されている表現の内容は変更されません。代わりにこの機能を使用して、正規表現の内容の解析中に、どのようなステップが実行されているかを MATLAB にレポートさせることができます。この機能は正規表現を診断する際に便利です。

次の例は、ゼロ以上の文字の後に 2 つの同一の文字が続き、さらにゼロ以上の文字が続く部分が解析されます。

regexp('mississippi', '\w*(\w)\1\w*', 'match')
ans =

  1×1 cell array

    {'mississippi'}

一致を決定する際に MATLAB がとるステップを厳密に追跡するために、この例では短いスクリプト (?@disp($1)) を挿入して、最終的に一致する文字を表示します。この例では最長一致の量指定子が使用されているので、MATLAB では文字ベクトルでできるだけ多くの部分を一致するように試みられます。つまり、文字列の始めで一致が見つかっても、MATLAB では文字列の最後に到達するまで検索が続けられます。その後、文字 i から p と次の p に戻り、一致が最終的に満たされるのでその点で停止します。

regexp('mississippi', '\w*(\w)(?@disp($1))\1\w*', 'match')
i
p
p

ans =

  1×1 cell array

    {'mississippi'}

次に、再び同じ例を使って、今度は最初の最短一致量指定子 (*?) を作成します。ここでも、MATLAB によって同じ一致が行われます。

regexp('mississippi', '\w*?(\w)\1\w*', 'match')
ans =

  1×1 cell array

    {'mississippi'}

ただし、動的スクリプトを挿入することによって、MATLAB によるテキストの一致がかなり異なっていることがわかります。この場合、MATLAB では見つかった最初の一致が使用され、テキストの残りは考慮されません。

regexp('mississippi', '\w*?(\w)(?@disp($1))\1\w*', 'match')
m
i
s

ans =

  1×1 cell array

    {'mississippi'}

このような動的表現の柔軟性を示すために、次の例では、MATLAB で入力テキストを繰り返し解析して、cell 配列を徐々に作成します。表現の最後にある演算子 (?!) は、実際には空の先読み演算子で、各繰り返しで強制的に失敗になります。MATLAB が表現の解決のためにとるステップを追跡するには、この強制的な失敗が必要です。

MATLAB では、入力テキストが何回も検索され、毎回異なる文字の組み合わせを試して、前回に見つかった一致よりも近い一致がないかどうかが確認されます。検索で一致が見つからない場合、検査結果は空の文字ベクトルになります。動的スクリプト (?@if(~isempty($&))) は、cell 配列 matches から空の文字ベクトルを省略するように処理します。

matches = {};
expr = ['(Euler\s)?(Cauchy\s)?(Boole)?(?@if(~isempty($&)),' ...
   'matches{end+1}=$&;end)(?!)'];

regexp('Euler Cauchy Boole', expr);

matches
matches =

  1×6 cell array

    {'Euler Cauchy Bo…'}    {'Euler Cauchy '}    {'Euler '}    {'Cauchy Boole'}    {'Cauchy '}    {'Boole'}

$& (または等価な $0)、$` および $' の各演算子は、それぞれ入力テキストの現在の一致場所、その前にあるすべての文字、その後ろにあるすべての文字を示します。これらの演算子は、動的表現 (特に、(?@cmd) 演算子を使用する場合) を扱うときに便利なことがあります。

次の例では、入力テキストを解析して文字 g を探します。テキストでの検索の各反復において、regexp によって現在の文字が g と比較され、見つからない場合は次の文字に進みます。この例では、^ 文字を使用して解析中の現在の位置をマークすることで、テキストのスキャンの過程を追跡します。

($` 演算子と 演算子では、現在解析している位置の前と後のテキストがキャプチャされます。 をテキストの中で指定する場合は、$'' のように一重引用符が 2 つ必要です。)

chr = 'abcdefghij';
expr = '(?@disp(sprintf(''starting match: [%s^%s]'',$`,$'')))g';

regexp(chr, expr, 'once');
starting match: [^abcdefghij]
starting match: [a^bcdefghij]
starting match: [ab^cdefghij]
starting match: [abc^defghij]
starting match: [abcd^efghij]
starting match: [abcde^fghij]
starting match: [abcdef^ghij]

置換式内のコマンド — ${cmd}

${cmd} 演算子は正規表現の置換パターンの内容を変更し、毎回変化する入力テキストのパラメーターに置換パターンを適応させます。MATLAB で使用される他の動的表現と同様に、全体的な置換式の中にこれらの表現をいくつでも含めることができます。

置換式内のコマンドは変数のローカル ワークスペースのみをチェックします。呼び出し側とグローバル ワークスペースを置換式内のコマンドで使用することはできません。

ここに示す regexprep の呼び出しでは、置換パターンは '${convertMe($1,$2)}' です。この場合、置換パターン全体が動的表現です。

regexprep('This highway is 125 miles long', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}');

この動的表現では、convertMe の呼び出しにおける入力引数として、一致したテキストから導かれた 2 つのトークン (\d+\.?\d*)(\w+) を使用して、convertMe という名前の関数を実行するように、MATLAB に指示が送られます。$1$2 の値は実行時に生成されるので、置換パターンは動的表現を必要とします。

次の例では、長さや量を英単位系からメートル法に変換する convertMe という名前のファイルを定義します。

function valout  = convertMe(valin, units)
switch(units)
    case 'inches'
        fun = @(in)in .* 2.54;
        uout = 'centimeters';
    case 'miles'
        fun = @(mi)mi .* 1.6093;
        uout = 'kilometers';
    case 'pounds'
        fun = @(lb)lb .* 0.4536;
        uout = 'kilograms';
    case 'pints'
        fun = @(pt)pt .* 0.4731;
        uout = 'litres';
    case 'ounces'
        fun = @(oz)oz .* 28.35;
        uout = 'grams';
end
val = fun(str2num(valin));
valout = [num2str(val) ' ' uout];
end

コマンド ラインで regexprep から関数 convertMe を呼び出し、変換する量と英単位系の名前の値を渡します。

regexprep('This highway is 125 miles long', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')
ans =

    'This highway is 201.1625 kilometers long'
regexprep('This pitcher holds 2.5 pints of water', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')
ans =

    'This pitcher holds 1.1828 litres of water'
regexprep('This stone weighs about 10 pounds', ...
          '(\d+\.?\d*)\W(\w+)', '${convertMe($1,$2)}')
ans =

    'This stone weighs about 4.536 kilograms'

以前の節で述べた (??@ ) 演算子と同様に、演算子 ${ } は現在アクティブなワークスペースの変数にアクセスできます。次の regexprep コマンドでは、ベース ワークスペースで定義された配列 A を使用しています。

A = magic(3)
A =

     8     1     6
     3     5     7
     4     9     2
regexprep('The columns of matrix _nam are _val', ...
          {'_nam', '_val'}, ...
          {'A', '${sprintf(''%d%d%d '', A)}'})
ans =

    'The columns of matrix A are 834 159 672'

参考

| |

関連するトピック