home changes contents help options

043:正規表現にマッチした部分の前後を取り出す

正規表現オブジェクト の start, end, string をつかう

Python では Ruby のようにメソッド 1 つで「正規表現にマッチした部分の前後」を取り出すことはできません。 しかし、そのような関数を作るのは難しくありません。次は正規表現オブジェクトを与えるとマッチした部分の前と後を返す関数です。:

   def pre_match(r):
       return r.string[:r.start()]

   def post_match(r):
       return r.string[r.end():]

使用してみます。:

   >>> import re
   >>> r = re.search(ur'生きのこる', u'この先生きのこるには')
   >>> print pre_match(r)
   この先
   >>> print post_match(r)
   には

正規表現オブジェクトは start, end というメソッドを持っています。引数なしで呼び出すと正規表現にマッチした部分の 先頭、末尾のインデックスを返します。

また、正規表現オブジェクトは string という属性を持っています。マッチ対象となった文字列を保持してます。

これらを組み合わせると、マッチした部分の前後を取り出すことができます。

サブグループの前後の取り出し

正規表現オブジェクトの start, end メソッドに整数 group を渡すと group 番目のサブグループの先頭、末尾のインデックスを得ることができます。 これを利用してサブグループの前後も取り出せるようにするには次のようにするとよいでしょうか? グループが存在はしているがマッチに関与していなかった場合の扱いをどうするか迷いましたが、 今回はエラーとするようにしています。:

   def pre_match2(r, group=0):
       index = r.start(group)
       if index < 0:
           raise IndexError('"group %d" exists but did not contribute to the match' % group)
       return r.string[:index]

   def post_match2(r, group=0):
       index = r.end(group)
       if index < 0:
           raise IndexError('"group %d" exists but did not contribute to the match' % group)
       return r.string[index:]

使用してみます。以下のコードでは文字列 "cba" に対し、正規表現 "b(c?)(a)" と "b(c)?(a)" を適用し、1・2・3番目のサブグループの前後を取り出そうとしています。

"b(c?)a" の1番目のグループである (c?) は b と a の間にマッチしています。 よって start, end メソッドの戻り値はともに 2 となります。 3番目のグループは存在していないため start, end メソッドを読んだ時点で IndexError? となります

対して "b(c)?a" の1番目のグループ (c) はマッチに関与していません。 start, end メソッドの戻り値は -1 となり、その後の if 文にて raise IndexError? されます。 3番目のグループは存在していないため IndexError? です。:

   >>> def print_prepost(r, groups):
   ...     for i in range(1, groups + 1):
   ...         print '---- group %d ----' % i
   ...         try:
   ...             print 'pre  : ' + repr(pre_match2(r, i))
   ...         except IndexError, e:
   ...             print e
   ...         try:
   ...             print 'post : + 'repr(post_match2(r, i))
   ...         except IndexError, e:
   ...             print e
   ...         print
   ...
   >>> print_prepost(re.search(r'b(c?)(a)', 'cba'), 3)
   ---- group 1 ----
   pre  : 'cb'
   post : 'a'

   ---- group 2 ----
   pre  : 'cb'
   post : ''

   ---- group 3 ----
   no such group
   no such group

   >>> print_prepost(re.search(r'b(c)?(a)', 'cba'), 3)
   ---- group 1 ----
   "group 1" exists but did not contribute to the match
   "group 1" exists but did not contribute to the match

   ---- group 2 ----
   pre  : 'cb'
   post : ''

   ---- group 3 ----
   no such group
   no such group