20 正規表現

20.1 イントロダクション

正規表現は、テキストのマッチのための領域固有言語です。テキストのマッチのために小さなプログラムをゼロから作ることもできますが、間違いを起こしやすいですし、面倒くさいですし、あまりポータブルでもフレキシブルでもありません。

代わりにマッチを(シンプルなケースでは)マッチする文字タイプとどれだけの文字をマッチさせたいかを決める数量詞を文字列として表現する正規表現を使います。

例えば、普通の文字と数字は文字通りマッチします。\wは単語の文字にマッチし、\sは(スペース、タブ、改行など)の空白文字にマッチします。ピリオド(.)は(改行をのぞいて)あらゆる文字にマッチします。

基本的な数量詞は、マッチがゼロ回かそれ以上あるアスタリスク(*)、1回かそれ以上nマッチするプラス(+)、{min,max}という形で表される範囲です。

これだけで、語を探す(\w+)機能や画像タグの中のalt属性(<img.*alt=".*">)を探す機能が手に入ります。

もっと長いテキストのマッチも必要ですが、マッチの部分集合(subset)が必要だと感じることも多いと思います。例えば、上記の例では、もしalt属性のテキストを置換したいとします。もし、括弧を使って、正規表現を囲むと、置換のための文字列のなかで使われうる変数を 捕まえる(キャプチャ) ことができます。置換のための文字列のフォーマットはこのセクションの最後で説明しますが、初めに捕まえたもの(キャプチャ)には、$1、2番目は$2を使います。

なので、alt属性のテキストを変えるためには、(<img.*alt=").*(">)を検索して、それを$1Text Intentionally Removed$2に置換できます。

上記の例では.*が使われています。しかしながら、アスタリスク演算子はどん欲(greedy)です。つまり、(マッチする限り)できるだけたくさんの文字にマッチします。なので、どん欲ではなくしたい場合は?を加えて.*?にします。

20.1.1 外部リソース

20.2 TextMateでの正規表現

これはTextMateが正規表現を活用できる機会のリストです:

  • (プロジェクトに加えられた)フォルダレファランスで表示されるファイルを正規表現を使うことによってフィルタリングする。
  • 検索とプロジェクト内の検索は正規表現を代わりに使うことができます。
  • 正規表現を使ってフォールディング(たたむ)マーカを 見つける
  • 正規表現のマッチに基づいて、インデントを計算する。
  • ランゲージグラマーは基本的には、それぞれのモードで、正規表現をもつ(サイクルつきの)木構造です。
  • スニペットでは正規表現を変数に適用できたり、(リアルタイムに)ミラーリングされたプレースホルダに適用できます。

よって、言うまでもなく正規表現はTextMateでは重要な役割をします。知らなくても幸せな生活を送ることができますが、(もしまだ詳しくないなら)本やチュートリアルなどを使って、正規表現により詳しくなることを強く推奨します。

TextMateだけではなく、(sed, grep, awk, findなどの)多くのシェルコマンドでは正規表現をサポートします。PerlやRubyのような有名なスクリプト言語には、言語の深いレベルで正規表現をサポートしています。

20.3 シンタックス (鬼車)

TextMateではK. Kosakoさんによる鬼車正規表現ライブラリを使います。

以下はhttp://www.geocities.jp/kosako3/oniguruma/doc/RE.ja.txtからの引用です。

    鬼車 正規表現 Version 5.6.0    2007/04/03

    使用文法: ONIG_SYNTAX_RUBY (既定値)


    1. 基本要素

      ¥       退避修飾 (エスケープ)  正規表現記号の有効/無効の制御
      |       選択子
      (...)   式集合   (グループ)
      [...]   文字集合 (文字クラス)


    2. 文字

      ¥t           水平タブ         (0x09)
      ¥v           垂直タブ         (0x0B)
      ¥n           改行             (0x0A)
      ¥r           復帰             (0x0D)
      ¥b           後退空白         (0x08)
      ¥f           改頁             (0x0C)
      ¥a           鐘               (0x07)
      ¥e           退避修飾         (0x1B)
      ¥nnn         八進数表現        符号化バイト値(の一部)
      ¥xHH         十六進数表現      符号化バイト値(の一部)
      ¥x{7HHHHHHH} 拡張十六進数表現  コードポイント値
      ¥cx          制御文字表現      コードポイント値
      ¥C-x         制御文字表現      コードポイント値
      ¥M-x         超  (x|0x80)      コードポイント値
      ¥M-¥C-x      超 + 制御文字表現 コードポイント値

      ※ ¥bは、文字集合内でのみ有効


    3. 文字種

      .        任意文字 (改行を除く)

      ¥w       単語構成文字

               Unicode以外の場合:
                 英数字, "_" および 多バイト文字。

               Unicodeの場合:
                 General_Category -- (Letter|Mark|Number|Connector_Punctuation)

      ¥W       非単語構成文字

      ¥s       空白文字

               Unicode以外の場合:
                 ¥t, ¥n, ¥v, ¥f, ¥r, ¥x20

               Unicodeの場合:
                 0009, 000A, 000B, 000C, 000D, 0085(NEL), 
                 General_Category -- Line_Separator
                                  -- Paragraph_Separator
                                  -- Space_Separator

      ¥S       非空白文字

      ¥d       10進数字

               Unicodeの場合: General_Category -- Decimal_Number

      ¥D       非10進数字

      ¥h       16進数字    [0-9a-fA-F]

      ¥H       非16進数字


      Character Property

        * ¥p{property-name}
        * ¥p{^property-name}    (negative)
        * ¥P{property-name}     (negative)

        property-name:

         + 全てのエンコーディングで有効
           Alnum, Alpha, Blank, Cntrl, Digit, Graph, Lower,
           Print, Punct, Space, Upper, XDigit, Word, ASCII,

         + EUC-JP, Shift_JISで有効
           Hiragana, Katakana

         + UTF8, UTF16, UTF32で有効
           Any, Assigned, C, Cc, Cf, Cn, Co, Cs, L, Ll, Lm, Lo, Lt, Lu,
           M, Mc, Me, Mn, N, Nd, Nl, No, P, Pc, Pd, Pe, Pf, Pi, Po, Ps,
           S, Sc, Sk, Sm, So, Z, Zl, Zp, Zs, 
           Arabic, Armenian, Bengali, Bopomofo, Braille, Buginese,
           Buhid, Canadian_Aboriginal, Cherokee, Common, Coptic,
           Cypriot, Cyrillic, Deseret, Devanagari, Ethiopic, Georgian,
           Glagolitic, Gothic, Greek, Gujarati, Gurmukhi, Han, Hangul,
           Hanunoo, Hebrew, Hiragana, Inherited, Kannada, Katakana,
           Kharoshthi, Khmer, Lao, Latin, Limbu, Linear_B, Malayalam,
           Mongolian, Myanmar, New_Tai_Lue, Ogham, Old_Italic, Old_Persian,
           Oriya, Osmanya, Runic, Shavian, Sinhala, Syloti_Nagri, Syriac,
           Tagalog, Tagbanwa, Tai_Le, Tamil, Telugu, Thaana, Thai, Tibetan,
           Tifinagh, Ugaritic, Yi



    4. 量指定子

      欲張り

        ?       一回または零回
        *       零回以上
        +       一回以上
        {n,m}   n回以上m回以下
        {n,}    n回以上
        {,n}    零回以上n回以下 ({0,n})
        {n}     n回

      無欲

        ??      一回または零回
        *?      零回以上
        +?      一回以上
        {n,m}?  n回以上m回以下
        {n,}?   n回以上
        {,n}?   零回以上n回以下 (== {0,n}?)

      強欲 (欲張りで、繰り返しに成功した後は回数を減らすような後退再試行をしない)

        ?+      一回または零回
        *+      零回以上
        ++      一回以上

        ({n,m}+, {n,}+, {n}+ は、ONIG_SYNTAX_JAVAでのみ強欲な指定子)

        例. /a*+/ === /(?>a*)/


    5. 錨

      ^       行頭
      $       行末
      ¥b      単語境界
      ¥B      非単語境界
      ¥A      文字列先頭
      ¥Z      文字列末尾、または文字列末尾の改行の直前
      ¥z      文字列末尾
      ¥G      照合開始位置


    6. 文字集合

      ^...    否定   (最低優先度演算子)
      x-y     範囲   (xからyまで)
      [...]   集合   (文字集合内文字集合)
      ..&&..  積演算 (^の次に優先度が低い演算子)

         例. [a-w&&[^c-g]z] ==> ([a-w] and ([^c-g] or z)) ==> [abh-w]

      ※ '[', '-', ']'を、文字集合内で通常文字の意味で使用したい場合には、
         これらの文字を'¥'で退避修飾しなければならない。


      POSIXブラケット ([:xxxxx:], 否定 [:^xxxxx:])

        Unicode以外の場合:

          alnum    英数字
          alpha    英字
          ascii    0 - 127
          blank    ¥t, ¥x20
          cntrl
          digit    0-9
          graph    多バイト文字全部を含む
          lower
          print    多バイト文字全部を含む
          punct
          space    ¥t, ¥n, ¥v, ¥f, ¥r, ¥x20
          upper
          xdigit   0-9, a-f, A-F
          word     英数字, "_" および 多バイト文字

        Unicodeの場合:

          alnum    Letter | Mark | Decimal_Number
          alpha    Letter | Mark
          ascii    0000 - 007F
          blank    Space_Separator | 0009
          cntrl    Control | Format | Unassigned | Private_Use | Surrogate
          digit    Decimal_Number
          graph    [[:^space:]] && ^Control && ^Unassigned && ^Surrogate
          lower    Lowercase_Letter
          print    [[:graph:]] | [[:space:]]
          punct    Connector_Punctuation | Dash_Punctuation | Close_Punctuation |
                   Final_Punctuation | Initial_Punctuation | Other_Punctuation |
                   Open_Punctuation
          space    Space_Separator | Line_Separator | Paragraph_Separator |
                   0009 | 000A | 000B | 000C | 000D | 0085
          upper    Uppercase_Letter
          xdigit   0030 - 0039 | 0041 - 0046 | 0061 - 0066
                   (0-9, a-f, A-F)
          word     Letter | Mark | Decimal_Number | Connector_Punctuation



    7. 拡張式集合

      (?#...)           注釈
      (?imx-imx)        孤立オプション
                          i: 大文字小文字照合
                          m: 複数行
                          x: 拡張形式
      (?imx-imx:式)     式オプション

      (式)              捕獲式集合
      (?:式)            非捕獲式集合

      (?=式)            先読み
      (?!式)            否定先読み
      (?<=式)           戻り読み
      (?<!式)           否定戻り読み

                        戻り読みの式は固定文字長でなければならない。
                        しかし、最上位の選択子だけは異なった文字長が許される。
                        例. (?<=a|bc) は許可. (?<=aaa(?:b|cd)) は不許可

                        否定戻り読みでは、捕獲式集合は許されないが、
                        非捕獲式集合は許される。

      (?>式)            原子的式集合
                        式全体を通過したとき、式の中での後退再試行を行なわない

      (?<name>式), (?'name'式)
                        名前付き捕獲式集合
                        式集合に名前を割り当てる(定義する)。
                        (名前は単語構成文字でなければならない。)

                        名前だけでなく、捕獲式集合と同様に番号も割り当てられる。
                        番号指定が禁止されていない状態 (10. 捕獲式集合 を参照)
                        のときは、名前を使わないで番号でも参照できる。

                        複数の式集合に同じ名前を与えることは許されている。
                        この場合には、この名前を使用した後方参照は可能であるが、
                        部分式呼出しはできない。


    8. 後方参照

      ¥n          番号指定参照 (n >= 1)
      ¥k<name>    名前指定参照
      ¥k'name'    名前指定参照

      名前指定参照で、その名前が複数の式集合で多重定義されている場合には、
      番号の大きい式集合から優先的に参照される。
      (マッチしないときには番号の小さい式集合が参照される)

      ※ 番号指定参照は、名前付き捕獲式集合が定義され、
         かつ ONIG_OPTION_CAPTURE_GROUPが指定されていない場合には、
         禁止される。(10. 捕獲式集合 を参照)


      ネストレベル付き後方参照

        ¥k<name+n>     n: 0, 1, 2, ...
        ¥k<name-n>     n: 0, 1, 2, ...
        ¥k'name+n'     n: 0, 1, 2, ...
        ¥k'name-n'     n: 0, 1, 2, ...

        後方参照の位置から相対的な部分式呼出しネストレベルを指定して、そのレベルでの
        捕獲値を参照する。

        例-1.

          /¥A(?<a>|.|(?:(?<b>.)¥g<a>¥k<b+0>))¥z/.match("reer")

        例-2.

          r = Regexp.compile(<<'__REGEXP__'.strip, Regexp::EXTENDED)
          (?<element> ¥g<stag> ¥g<content>* ¥g<etag> ){0}
          (?<stag> < ¥g<name> ¥s* > ){0}
          (?<name> [a-zA-Z_:]+ ){0}
          (?<content> [^<&]+ (¥g<element> | [^<&]+)* ){0}
          (?<etag> </ ¥k<name+1> >){0}
          ¥g<element>
          __REGEXP__

          p r.match('<foo>f<bar>bbb</bar>f</foo>').captures



    9. 部分式呼出し ("田中哲スペシャル")

      ¥g<name>    名前指定呼出し
      ¥g'name'    名前指定呼出し
      ¥g<n>       番号指定呼出し (n >= 1)
      ¥g'n'       番号指定呼出し (n >= 1)

      ※ 最左位置での再帰呼出しは禁止される。
         例. (?<name>a|¥g<name>b)   => error
             (?<name>a|b¥g<name>c)  => OK

      ※ 番号指定呼出しは、名前付き捕獲式集合が定義され、
         かつ ONIG_OPTION_CAPTURE_GROUPが指定されていない場合には、
         禁止される。 (10. 捕獲式集合 を参照)

      ※ 呼び出された式集合のオプション状態が呼出し側のオプション状態と異なっている
         とき、呼び出された側のオプション状態が有効である。

         例. (?-i:¥g<name>)(?i:(?<name>a)){0} は "A" に照合成功する。


    10. 捕獲式集合

      捕獲式集合(...)は、以下の条件に応じて振舞が変化する。
      (名前付き捕獲式集合は変化しない)

      case 1. /.../     (名前付き捕獲式集合は不使用、オプションなし)

         (...) は、捕獲式集合として扱われる。

      case 2. /.../g    (名前付き捕獲式集合は不使用、オプション 'g'を指定)

         (...) は、非捕獲式集合として扱われる。

      case 3. /..(?<name>..)../   (名前付き捕獲式集合は使用、オプションなし)

         (...) は、非捕獲式集合として扱われる。
         番号指定参照/呼び出しは不許可。

      case 4. /..(?<name>..)../G  (名前付き捕獲式集合は使用、オプション 'G'を指定)

         (...) は、捕獲式集合として扱われる。
         番号指定参照/呼び出しは許可。

      但し
        g: ONIG_OPTION_DONT_CAPTURE_GROUP
        G: ONIG_OPTION_CAPTURE_GROUP
        ('g'と'G'オプションは、ruby-dev MLで議論された。)

      これらの振舞の意味は、
      名前付き捕獲と名前無し捕獲を同時に使用する必然性のある場面は少ないであろう
      という理由から考えられたものである。


    -----------------------------
    補記 1. 文法依存オプション

       + ONIG_SYNTAX_RUBY
         (?m): 終止符記号(.)は改行と照合成功

       + ONIG_SYNTAX_PERL と ONIG_SYNTAX_JAVA
         (?s): 終止符記号(.)は改行と照合成功
         (?m): ^ は改行の直後に照合する、$ は改行の直前に照合する


    補記 2. 独自拡張機能

       + 16進数数字、非16進数字  ¥h, ¥H
       + 名前付き捕獲式集合      (?<name>...), (?'name'...)
       + 名前指定後方参照        ¥k<name>
       + 部分式呼出し            ¥g<name>, ¥g<group-num>


    補記 3. Perl 5.8.0と比較して存在しない機能

       + ¥N{name}
       + ¥l,¥u,¥L,¥U, ¥X, ¥C
       + (?{code})
       + (??{code})
       + (?(condition)yes-pat|no-pat)

       * ¥Q...¥E
         但しONIG_SYNTAX_PERLとONIG_SYNTAX_JAVAでは有効


    補記 4. Ruby 1.8 の日本語化 GNU regex(version 0.12)との違い

       + 文字Property機能追加 (¥p{property}, ¥P{Property})
       + 16進数字タイプ追加 (¥h, ¥H)
       + 戻り読み機能を追加
       + 強欲な繰り返し指定子を追加 (?+, *+, ++)
       + 文字集合の中の演算子を追加 ([...], &&)
         ('[' は、文字集合の中で通常の文字として使用するときには
          退避修飾しなければならない)
       + 名前付き捕獲式集合と、部分式呼出し機能追加
       + 多バイト文字コードが指定されているとき、
         文字集合の中で八進数または十六進数表現の連続は、多バイト符合で表現された
         一個の文字と解釈される
         (例. [¥xa1¥xa2], [¥xa1¥xa7-¥xa4¥xa1])
       + 文字集合の中で、一バイト文字と多バイト文字の範囲指定は許される。
         ex. /[a-あ]/
       + 孤立オプションの有効範囲は、その孤立オプションを含んでいる式集合の
         終わりまでである
         例. (?:(?i)a|b) は (?:(?i:a|b)) と解釈される、(?:(?i:a)|b)ではない
       + 孤立オプションはその前の式に対して透過的ではない
         例. /a(?i)*/ は文法エラーとなる
       + 不完全な繰り返し範囲指定子は通常の文字列として許可される
         例. /{/, /({)/, /a{2,3/
       + 否定的POSIXブラケット [:^xxxx:] を追加
       + POSIXブラケット [:ascii:] を追加
       + 先読みの繰り返しは不許可
         例. /(?=a)*/, /(?!b){5}/
       + 数値で指定された文字に対しても、大文字小文字照合オプションは有効
         例. /¥x61/i =~ "A"
       + 繰り返し回数指定で、最低回数の省略(0回)ができる
         /a{,n}/ == /a{0,n}/
         最低回数と最大回数の同時省略は許されない。(/a{,}/)
       + /a{n}?/は無欲な演算子ではない。
         /a{n}?/ == /(?:a{n})?/
       + 無効な後方参照をチェックしてエラーにする。
         /¥1/, /(a)¥2/
       + 無限繰り返しの中で、長さ零での照合成功は繰り返しを中断させるが、
         このとき、中断すべきかどうかの判定として、捕獲式集合の捕獲状態の
         変化まで考慮している
         /(?:()|())*¥1¥2/ =~ ""
         /(?:¥1a|())*/ =~ "a"



    補記 5. 実装されているが、既定値では有効にしていない機能

       + 捕獲履歴参照

         (?@...) と (?@<name>...)

         例. /(?@a)*/.match("aaa") ==> [<0-1>, <1-2>, <2-3>]

         使用方法は、sample/listcap.cを参照

         有効にしていない理由は、どの程度役に立つかはっきりしないため。


    補記 6. 問題点

       + UTF-8で、バイト値が適正な価かどうかのチェックは行なっていない。

         * 先頭バイトとして不正なバイトを一文字とみなす
           /./u =~ "¥xa3"

         * 不完全なバイトシーケンスのチェックをしない
          /¥w+/ =~ "a¥xf3¥x8ec"

         これを調べることは可能ではあるが、遅くなるので行なわない。

    終り

20.4 置換文字列シンタックス(フォーマット文字列)

正規表現の置換を使うとき、置換のための文字列は、キャプチャを参照したり、ケースフォールディングを実行したり、(キャプチャのレジスタに基づいて)条件付きの挿入をしたり、最小限のエスケープ文字列をサポートするフォーマット文字列として解釈されます。

20.4.1 キャプチャ

キャプチャを参照するには、$nを使ってください(nはキャプチャレジスタの番号です)。$0はマッチ全体を意味します。

用例:

   検索: <img src="(.*?)">
置換: <img src="$1" alt="$1">

20.4.2 コードフォールディング

\u\lを先頭に追加することによってその次文字を大文字に変えたり、小文字に変えたりすることができます。これは、主に、その次の文字がキャプチャレジスタに由来するときに便利です。用例:

   検索: (<a.*?>)(.*?)(</a>)
置換: $1\u$2$3

より長い文字列を\U\Lを使って大文字や小文字に変換できます。Eを使って、コードフォールディングを無効にできます。用例:

   検索: (<a.*?>)(.*?)(</a>)
置換: $1\U$2\E$3

20.4.3 条件付きの挿入

置換が何かがマッチしたかどうかによることがあります。これは、キャプチャ«n»がマッチしたら、«insertion»を挿入するために、(?«n»:«insertion»)を使えばできます。キャプチャ«n»がマッチしなかったら、«otherwise»を挿入するために(?«n»:«insertion»:«otherwise»)を使うこともできます。

キャプチャを条件付きにするには、foo|(bar)|fudのように変形の中に置いてください。もしくは、(bar)?のようにクエスチョンマークを加えることができます。(.*)ゼロ個の文字でもマッチしてしまうので、代わりに(.+)?を使ってください。

例えば、もし文字五文字以上の時に、8文字に切り詰めて、省略記号を挿入したい時には、、以下のようにします:

   検索: (\w+(?:\W+\w+){,7})\W*(.+)?
   置換: $1(?2:…)

ここではまず、それぞれの語が語ではない文字(スペースに)先行された、7つの語((?:\W+\w+){,7})が後にくる(\w+)にマッチさせます。その後、、任意で、キャプチャレジスタ2((.+)?)へ(語ではない文字によって区切られた)それに続くものは何でも書き入れます。

置換はまず、マッチした8文字まで($1)を挿入します。それからもしキャプチャ2が何かにしたら、省略記号((?2:…))を挿入します。

20.4.4 エスケープコード

ケースフォールディングのエスケープコードに加えて、\nで改行文字を、\tでタブ文字を、\$でドル文字を挿入できます。