忍者ブログ
     2008年11月14日 開始
[38] [1] [5] [6] [7] [8] [9] [10] [11]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

http://www.python.jp/Zope/articles/tips/regex_howto/regex_howto_4

4.3. 取り出さないグループと名前付きグループ

凝った正規表現を書こうとすると、文字列を取り出すためのグループと、正規表現自体を構造化するためのグループを使う場合があります。しかし、複雑な正規表現では、グループ番号を追いかけ続けるのが困難になります。この問題を解決するのに 2 つの方法があります。両方とも正規表現で拡張された、一般的な文法を使うので、まずそれを見ていきましょう。

Perl 5 では、標準の正規表現の他にいくつかの機能が加えられ、Python の re モジュールはそのほとんどをサポートしています。 Perl の正規表現と標準の正規表現が、混乱するような方法で違ったものにならないように、1 文字で入力できる新しいメタ文字や、新しい機能を表すために "\" で始まる特殊なシーケンスを選ぶことは難しかったものです。たとえば、"&" を新しいメタ文字にするとしましょう。ところが古い表記では "&" が通常の文字なので、\&[&] のようにエスケープされていないわけです。選ばれた解決策は、(?...) を拡張文法として使うことでした。括弧直後の "?" は繰り返す対象がないので、シンタックスエラーです。ですから、これは互換性の問題を発生しません。 "?" 直後の文字で、どの拡張が使うかを示すので、(?=foo)(?:foo) は別のものです(それぞれ a positive lookahead assertion と non-capturing group containing the subexpression foo)。

Python では、 Perl の拡張文法に、さらに拡張文法を追加されています。 Python で拡張されたものは、疑問符直後の最初の文字が "P" になっています。現在のところ、そのような拡張は 2 つあります。 (?P<name>...) は名前つきグループを定義し、(?P=name) は名前つきグループを後方参照します。将来 Perl 5 が似たような機能を別の文法で追加すれば、Python 独自の文法は互換性のために残しつつ、re は、Perl の文法をサポートする変更されるでしょう。

ここまで見てきた一般的な拡張文法を踏まえて、複雑な正規表現中のグループを扱うことを単純にする機能に話を戻しましょう。グループは左から右へと番号づけされ、複雑な正規表現では多くのグループを使います。したがって、正しい番号を追いかけるのが難しくなり、そのような複雑な正規表現を修正するのはやっかいです。先頭付近に新しいグループを挿入すれば、それ以降の全ての番号を変更することになるからです。

グループの内容を取得しないけれども、正規表現の一部を集めるために、グループを使いたい場合があります。これを明示するには、非取得グループを示す (?:...) を使います。括弧内にどんな正規表現でも配置できます。

>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()

マッチしたグループの内容を取得できないという事実を除けば、非取得グループは内容を取得できるグループとまったく同じように振舞います。この中に何か別の表現を入れたり、"*" 等の繰り返しメタ文字で繰り返したり、他のグループ (取得でも非取得でも) の入れ子にしたりすることもできます。また、他のグループの番号づけを変更することなく、新しいグループを追加できるので、既に存在するグループを修正するときに (?:...) は特に役に立ちます。グループの内容を取得してもしなくても、探索の性能に違いはありません。どちらも同じ速さで動作します。

より重要な機能は名前付きグループです。グループを番号で参照するのではなく、名前で参照できるのです。

名前付きグループの文法は Python の独自拡張のひとつで、(?P<name>...) と表記します。 name は、もちろん、グループの名前です。グループを名前で関連づけることを除けば、名前付きグループは取得グループと同じように振る舞います。取得グループを扱う、全ての MatchObject のメソッドは、グループに付けられた番号を参照するために整数か、グループ名を受け付けます。名前付きグループは番号付けもされているので、2 通りの方法で、グループの情報を取得することができるのです。

>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'

名前付きグループを使えば、番号を覚える代わりに、覚えやすい名前で参照できるので便利です。 imaplib モジュールから、正規表現の例を示します。

InternalDate = re.compile(r'INTERNALDATE "'
        r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
        r'(?P<year>[0-9][0-9][0-9][0-9])'
        r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
        r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
        r'"')

m.group('zonem') を取得するほうが、グループ番号 9 を覚えるより、とても簡単です。

(...)\1 のような表記では、後方参照のための文法がグループ番号を参照するので、名前を使う方法ではと自然と不一致が生じます。そこで、Python 独自拡張には (?P=name) があります。これは、name と名付けられたグループの内容が、現在位置に存在することを示します。繰り返される単語を見付ける正規表現、(\b\w+)\s+\1 は、(?P<word>\b\w+)\s+(?P=word) と書くこともできるのです。

>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
>>> p.search('Paris in the the spring').group()
'the the'
PR

コメント


コメントフォーム
お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード
  Vodafone絵文字 i-mode絵文字 Ezweb絵文字


トラックバック
この記事にトラックバックする:


忍者ブログ [PR]
お天気情報
カレンダー
03 2024/04 05
S M T W T F S
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
リンク
フリーエリア
最新CM
最新TB
プロフィール
HN:
No Name Ninja
性別:
非公開
バーコード
ブログ内検索
P R
カウンター
ブログの評価 ブログレーダー