2008年11月14日 開始
× [PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。 http://www.python.jp/Zope/articles/tips/regex_howto/regex_howto_3
|
文字列 | 段階 |
---|---|
\section |
マッチさせるテキスト文字列 |
\\section |
re.compile 用にバックスラッシュをエスケープ |
"\\\\section" |
文字列リテラル用にバックスラッシュをエスケープ |
つまり、リテラルのバックスラッシュにマッチさせるには、正規表現文字列として '\\\\'
と書かなければいけません。なぜなら正規表現は "\\
" でなくてはいけませんし、Python の文字列リテラル中で、各バックスラッシュは "\\
" と書かれなければいけないからです。バックスラッシュが繰り返し現れる正規表現中では、これが多くのバックスラッシュの繰り返しを誘発することになり、その文字列を理解することが困難になります。
解決策は Python の生の文字列(raw string)表記を正規表現に使うことです。 "r
" を前置した文字列リテラル内では、バックスラッシュは特別な扱いを受けません。ですから r"\n"
は、"\
" と "n
"の 2 文字からなる文字列ですが、"\n"
は 1 つの改行文字です。 Python のコードで正規表現を書くとき、この生の文字列表記がよく用いられます。
一般の文字列 | 生の文字列 |
---|---|
"ab*" |
r"ab*" |
"\\\\section" |
r"\\section" |
"\\w+\\s+\\1" |
r"\w+\s+\1" |
3.3. マッチングを行う
ここまでで、コンパイルされた正規表現を represent するオブジェクトを持っているのですが、これで何をすればいいのでしょう。 RegexObject
インスタンスは、いくつかのメソッドと属性を持っています。最も重要なものだけをここで扱います。完全なリストを手に入れるには、ライブラリ・リファレンスをあたってください。
メソッド / 属性 | 目的 |
---|---|
match |
正規表現が、文字列の先頭でマッチするかどうか調べる |
search |
文字列を走査し、正規表現がマッチする位置を探す |
split |
正規表現がマッチする全ての位置で、文字列をリストに分割する |
sub |
正規表現がマッチする全ての部分文字列を見つけ出し、別の文字列に置換する |
subn |
sub() と同じ。ただし置換の個数を制限することができる |
全くマッチしなければ、これらのメソッドは None
を返します。見つかれば MatchObject
インスタンスを返します。このインスタンスは、マッチした部分文字列や、それがどこで始まり、どこで終わるか、などの情報をもっています。
re
モジュールを使って、対話的に実験することで勉強していきましょう。 (Tkinter が使えるなら redemo.py を見るのもよいでしょう。これは Python ディストリビューションに含まれているデモです。正規表現を入力すると、マッチしたか失敗したかを表示してくれるのです。コンパイルされた正規表現をデバッグしようとするときには、redemo.py が非常に有用であると思われます)
最初に、Python インタプリタを起動して、re
をインポートしてから、正規表現をコンパイルします。
Python 1.5.1 (#6, Jul 17 1998, 20:38:08) [GCC 2.7.2.3] on linux2 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> import re >>> p = re.compile('[a-z]+') >>> p <re.RegexObject instance at 80c3c28>
これで正規表現 [a-z]+
に対して、様々な文字列のマッチングを試みることができます。 +
は「1 回または、それ以上の繰り返し」を意味するので、空の文字列はまったくマッチしないはずです。この場合 match()
の戻り値 None
はインタプリタに何の出力も表示させません。結果をはっきりさせたければ、明示的に match()
の結果をを表示すこともできます。
>>> p.match("") >>> print p.match("") None
今度は、"tempo
" のような、マッチする文字列を試してみましょう。この場合、match()
は MatchObject
を返すので、後で使えるように、この結果を変数に保存しておきます。
>>> m = p.match('tempo') >>> print m <re.MatchObject instance at 80c4f68>
この MatchObject
から、マッチした文字列に関する情報を得ることができます。 MatchObject
インスタンスには、いくつかのメソッドと属性があります。そのうち特に重要なものは以下のとおりです。
メソッド / 属性 | 目的 |
---|---|
group() |
正規表現にマッチした文字列を返す |
start() |
マッチした文字列が始まる位置を返す |
end() |
マッチした文字列が終わる位置を返す |
span() |
マッチした文字列の位置を (start, end) のタプルで返す |
これらのメソッドを使ってみれば、どういうことか明確になるでしょう。
>>> m.group() 'tempo' >>> m.start(), m.end() (0, 5) >>> m.span() (0, 5)
group()
は正規表現にマッチした部分文字列を返します。 start()
と end()
は、マッチした部分の開始位置と終了位置を返します。 span()
は開始位置と終了位置の両方を、タプルにして返します。 match
メソッドは、正規表現が文字列の先頭でマッチするかどうかをチェックするだけなので、start()
の戻り値は常に 0 です。しかし RegexObject
インスタンスの search
メソッドは、文字列全部に渡って調べるので、マッチの開始位置が 0 でない場合もあります。
>>> print p.match('::: message') None >>> m = p.search('::: message') ; print m <re.MatchObject instance at 80c9650> >>> m.group() 'message' >>> m.span() (4, 11)
実際のプログラムでは、MatchObject
を変数に保存し、その値が None
かどうかチェックするのが最も一般的なスタイルで、次のようなになります。
p = re.compile( ... ) m = p.match('string goes here') if m: print 'Match found: ', m.group() else: print 'No match'
3.4. モジュールレベルの関数
必ずしも RegexObject
を作ったり、そのメソッドを呼び出す必要はありません。 re
モジュールは、match()
, search()
, sub()
, などのトップレベル関数も提供します。これらの関数は、RegexObject
メソッドと対応していて、第 1 引数が正規表現文字列であるような引数を取り、なおかつ None
または MatchObject
インスタンスを返します。
>>> print re.match(r'From\s+', 'Fromage amk') None >>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998') <re.MatchObject instance at 80c5978>
この隠れ蓑の下で、これらの関数はあなたに代わって RegexObject
を作り、それに対して適切なメソッドを呼び出すだけです。また、コンパイルされたオブジェクトをキャッシュに保存し、後で、同じ正規表現をを使うときの呼び出しが速くなります。
モジュールレベルの関数を使うべきでしょうか、RegexObject
を作ってからメソッドを呼び出すべきでしょうか。どれだけ頻繁にその正規表現を使わうのか、あなたのコーディングスタイル次第です。もし正規表現がコードの中で、たった 1 箇所だけで使うのなら、モジュール関数がおそらく便利でしょう。一方、プログラムがたくさんの正規表現を包含していたり、同じ正規表現を別々の箇所で何度も使ったりするなら、全ての定義を始めの 1 箇所に集めて、正規表現をコンパイルする価値があるでしょう。標準ライブラリの xmllib.py を例にとってみましょう。
ref = re.compile( ... ) entityref = re.compile( ... ) charref = re.compile( ... ) starttagopen = re.compile( ... )
(私はふつう、1 回使うだけであっても、コンパイル済みオブジェクトを好みます。私と同じくらい、徹底するようになる人もいるようです)
3.5. コンパイル時のフラグ
コンパイル時にフラグを指定することで、正規表現が動作するときの振るまいを変更できます。 re
モジュールのフラグには、IGNORECASE
のようなロングネームと、I
のように1文字で表すショートネームが使えます。 (もし Perl のパターン修飾子に慣れているなら、ショートネームは Perl と同じ文字を使うと言えば分かりやすいかも知れません。 re.VERBOSE
のショートネームは re.X
です) 複数のフラグは、OR ビット演算で指定できます。たとえば、re.I | re.M
は、I
と M
の両方を指定することになります。
以下に使用できるフラグの表を示します。詳しい説明は後述します。
フラグ | 意味 |
---|---|
DOTALL , S |
. が改行も含めて、全ての文字とマッチするように指定する |
IGNORECASE , I |
大文字小文字を区別しない |
LOCALE , L |
ロケールを考慮してマッチングを行う |
MULTILINE , M |
複数行にマッチング。これは ^ と $ に影響する |
VERBOSE , X |
冗長な正規表現(もっと、きれいで分かりやすくまとめられる表現)を有効にする。 |
I
,IGNORECASE
- 大文字小文字を区別せずにマッチングを行う。文字クラスとリテラル文字列は大文字小文字を無視して、マッチングが行われる。例えば、
[A-Z]
は小文字にもマッチし、Spam
は、"Spam
"、"spam
"、"spAM
" にマッチします。このフラグ指定では、カレント・ロケールを考慮しません。
L
,LOCALE
\w
,\W
,\b
,\B
をカレント・ロケールに依存して扱います。ロケールは、扱う自然言語の違いを考慮に入れるのを手助けする目的で、C 言語ライブラリに含まれる機能です。たとえば、フランス語を処理するときには、単語にマッチさせるのに\w+
と書けることを望むでしょう。しかし、\w
は文字クラス[A-Za-z]
のみとマッチし、"é
" や "ç
" とはマッチしません。システムが適切に設定されていて、フランス語が選択されていれば、特定の C 言語の関数が、プログラムに対して "é
" も文字扱いするように教えるのです。正規表現をコンパイルするときにLOCALE
フラグを設定すると、コンパイル済みオブジェクトが\w
を扱うときに、こういう C 言語の関数を使うことになります。この場合処理は遅くなりますが、期待どおり\w+
がフランス語の単語にマッチします。M
,MULTILINE
- 通常、
^
は行頭にのみ、$
は(もし存在すれば)newline 直前の行末にのみマッチします。このフラグを指定すると、^
は文字列の先頭と、文字列中で newline の直後に続く行頭にマッチします。同様に、メタ文字$
は、文字列の最後と(newline 直前の)行末にマッチします。 S
,DOTALL
- 特殊文字 "
.
" が改行文字も含めて、あらゆる文字にマッチするようにします。このフラグを指定しないと、".
" は改行文字以外のあらゆる文字にマッチします。 X
,VERBOSE
- このフラグを指定すると、柔軟な書式で、正規表現を読みやすく書くことができます。文字クラスやエスケープされていないバックスラッシュで生成された空白文字を除いて、正規表現中の空白文字は無視されます。これにより、正規表現を分かりやすくまとめたり、インデントしたりできるのです。また、正規表現中にコメントを入れることもできます。文字クラスや、エスケープされていないバックスラッシュによって生成されている場合を除き、"
#
" 以降がコメントになります。コメントは単に無視されます。
re.VERBOSE
を使った正規表現の例を以下に示します。とても読みやすいと思いませんか?
charref = re.compile(r""" &# # 数値型の開始 (?P<char> [0-9]+[^0-9] # 10進法 | 0[0-7]+[^0-7] # 8進法 | x[0-9a-fA-F]+[^0-9a-fA-F] # 16進法 ) """, re.VERBOSE)
冗長表現を指定しないと、この正規表現は次のようになります。
charref = re.compile("&#(?P<char>[0-9]+[^0-9]" "|0[0-7]+[^0-7]" "|x[0-9a-fA-F]+[^0-9a-fA-F])")
上の例では、Python が文字列リテラルを自動的に連結するのを利用して、正規表現を小さな部分に分割していますが、それでも re.VERBOSE
を使った場合に比べて、分かりにくくなります。
10 | 2024/11 | 12 |
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 |