Tips

Match regexの使い方 - その1

日付2008/08/22
ID08-023
バージョン11
プラットフォームMac & Win

Match regexは、4D v11 SQLで採用されているICU(International Components for Unicode)が有するUnicodeに特化された正規表現を利用して文字列のパターンをマッチングするためのコマンドです。ちなみにPosition関数も内部的に同じルーチンを利用しています(アスタリスクを渡さなかった場合)。ICUの正規表現は、基本的に他の言語(Perl)などのそれと同じですが、Unicodeのテキストを評価する目的に特化されており、Unicodeではない文章やバイナリデータのサーチはできません。一方、Unicode特有の属性(プロパティ)に関連する正規表現が使用できるようになっています。

http://icu-project.org/userguide/regexp.html

もっとも基本的なMatch regexの使い方は、Position関数のそれとほとんど変わりません。つまり、テキスト内である文字列が出現する箇所を特定します。つまり次のコードはどちらも同じことをしています。

$Match_b:=Match regex("う";"あいうえお";1;$Position_l;$Length_l)
$Position_l:=Position("う";"あいうえお";1;$Length_l)

上記の例で"う"の長さが分かっているのにも関わらずLength_lを調べているのには理由があります。Unicodeでは、等価とみなされる文字をいろいろな仕方で表現でき、中には長さ(コードポイント数)が異なるものもあるからです。たとえば、"パターン"というファイル名で文書を保存したとします。Mac OS Xの場合、ファイルシステムのルールにより「パ」という一文字は「ハ」と半濁音に分離されます。つまり等価とみなされる文字でありながら、一方はLengthが1、他方はLengthが2になります。Match regexで合致したパターンを抽出するためには、その長さも正確に調べる必要があるのです。

http://unicode.org/reports/tr15/

Substring関数は、文字列をその長さ(コードポイント数)で切り出します。つまり、PositionやMatch regexで受け取った値をそのまま渡すことができます。たとえば次のようにパターンに合致した文字列を取り出すことができます。ここで$Length_lの代わりにLength("う")のような書き方をしていると、前述のような複数のコードポイントで構成される文字(「う」と濁点の組み合わせなど)が含まれていた場合に問題が生じます。

$Result_t:=Substring("あいうえお"; Position_l; Length_l)

Positionと同様、Match regexにはパターンを構成する文字をそのまま渡すことができます。ただし、Positionとは違い、次の文字はバックスラッシュ記号でエスケープしなくてはなりません。* ? + [ ( ) { } ^ $ | \ . /

Match regexの強みは、実際の文字(リテラル)だけではなく、豊富なメタキャラクターやオペレータでパターンを構成できることにあります。メタキャラクターはリテラルな文字の代わりに置かれるものです。オペレータはリテラルな文字またはメタキャラクターの後に置かれ、特定の条件を付与します。多くの場合、最初に覚えるのは次のメタキャラクターとオペレータです。

メタキャラクター

  • .   何らかの文字つまりワイルトカード。
  • \s   あらゆるスペース。
  • オペレータ

  • *   0個以上、最大限。
  • +   1個以上、最大限。
  • たとえば、次のパターンでは、空白を無視したい位置には\\s*を置き、データを読みたい箇所には.+を置いているので、空白の有無に関係なく、データがあればパターンにマッチします。

    $Match_b:=Match regex("\\s*content-length\\s*:\\s*.+\\s*";"  content-length : 123 ";1;$Position_l;$Length_l)

    ほとんどの場合、Match regexは、全体のパターンを評価するよりも、その中で特定のパターンにマッチする部分を取り出すために使用されます。つまり、上記の例では、全体として"content-length : ###"という形になっていることを調べるのが最終目的ではなく、###の値を調べること興味があるという意味です。Match regexで文字列の中から特定のパターンにマッチする部分を取り出すには、キャププチャリング括弧と位置/長さの配列を使用します。

    はじめに、正規表現のパターンをいくつかのブロックに分解し、それぞれを括弧で括ります。

    "(\\s*)content-length(\\s*):(\\s*)(.+)(\\s*)"

    上記の例では、全部で5個のブロックに分解されており、4番目のブロック(.+)には、"content-length : ###"の###の部分が入ります。

    ARRAY LONGINT(Position_al;0)
    ARRAY LONGINT(Length_al;0)
    
    $Match_b:=Match regex("(\\s*)content-length(\\s*):(\\s*)(.+)(\\s*)";"  content-length : 123 ";1;$Position_al;$Length_al)

    倍長整数の配列を宣言し、Match regexにパラメータとして渡すと、ブロックの数だけ要素を持つ配列のペアが返されます。それぞれの要素は各ブロックの開始位置と長さ(コードポイント数)に対応しています。"content-length : ###"の###の部分が入るのは4番目のブロック(.+)なので、###の値を取り出すのは次のようにSubstring関数を呼び出します。(もちろん、実践ではリテラルな文字列ではなく、テキスト型の変数に対してコマンドを実行します。)

    $Value_t:=Substring("  content-length : 123 "; Position_al{4}; Length_al{4})

    まとめ

  • Match regex/Positonを使う場合、合致する文字列同士のLengthが同じとは限りません。
  • 特定のパターンに合致する部分を取り出すには正規表現を括弧でグループに分けておきます。
  • 倍長整数の配列を宣言し、Match regexの結果を配列で受け取ります。
  • n番目のグループに合致する部分の開始位置と長さは配列のn番要素に代入されています。