6.2. re
— 正規表現操作¶
ソースコード: Lib/re.py
このモジュールは、Perl などと同様の正規表現マッチング操作を提供しています。
パターンと文字列には Unicode 文字列および 8 ビット文字列が使用できます。しかし、Unicode 文字列と8ビット文字列の混在はできません。すなわち、Unicode 文字列をバイト列のパターンでマッチングしたり、その逆はできません。同様に、置き換え時の置換文字列はパターンおよび検索文字列と同じ型でなくてはなりません。
正規表現では、特殊な形式を表したり、特殊文字の持つ特別な意味を呼び出さずにその特殊な文字を使えるようにするために、バックスラッシュ文字 ('\'
) を使います。こうしたバックスラッシュの使い方は、 Python の文字列リテラルにおける同じバックスラッシュ文字と衝突を起こします。例えば、バックスラッシュ自体にマッチさせるには、パターン文字列として '\\\\'
と書かなければなりません、というのも、正規表現は \\
でなければならず、さらに正規な Python 文字列リテラルでは各々のバックスラッシュを \\
と表現せねばならないからです。
正規表現パターンに Python の raw string 記法を使えばこの問題を解決できます。 'r'
を前置した文字列リテラル内ではバックスラッシュを特別扱いしません。従って、 "\n"
が改行一文字の入った文字列になるのに対して、 r"\n"
は '\'
と 'n'
という二つの文字の入った文字列になります。通常、 Python コード中では、パターンをこの raw string 記法を使って表現します。
大抵の正規表現操作が、モジュールレベルの関数と、 コンパイル済み正規表現 のメソッドとして提供されることに注意して下さい。関数は正規表現オブジェクトのコンパイルを必要としない近道ですが、いくつかのチューニング変数を失います。
参考
The third-party regex module,
which has an API compatible with the standard library re
module,
but offers additional functionality and a more thorough Unicode support.
6.2.1. 正規表現のシンタックス¶
正規表現 (すなわち RE) は、表現にマッチ (match) する文字列の集合を表しています。このモジュールの関数を使えば、ある文字列が指定の正規表現にマッチするか (または指定の正規表現がある文字列にマッチするか、つまりは同じことですが) を検査できます。
正規表現を連結すると新しい正規表現を作れます。 A と B がともに正規表現であれば AB も正規表現です。一般的に、文字列 p が A とマッチし、別の文字列 q が B とマッチすれば、文字列 pq は AB にマッチします。ただし、この状況が成り立つのは、 A と B との間に境界条件がある場合や、番号付けされたグループ参照のような、優先度の低い演算を A や B が含まない場合だけです。かくして、ここで述べるような、より簡単でプリミティブな正規表現から、複雑な正規表現を容易に構築できます。正規表現に関する理論と実装の詳細については上記の Friedl 本か、コンパイラの構築に関する教科書を調べて下さい。
以下で正規表現の形式に関する簡単な説明をしておきます。より詳細な情報やよりやさしい説明に関しては、 正規表現 HOWTO を参照してください。
正規表現には、特殊文字と通常文字の両方を含められます。 'A'
、 'a'
、あるいは '0'
のようなほとんどの通常文字は最も簡単な正規表現になります。こうした文字は、単純にその文字自体にマッチします。通常の文字は連結できるので、 last
は文字列 'last'
とマッチします。 (この節の以降の説明では、正規表現を引用符を使わずに この表示スタイル: special style
で書き、マッチ対象の文字列は、 '引用符で括って'
書きます。)
'|'
や '('
といったいくつかの文字は特殊文字です。特殊文字は通常の文字の種別を表したり、あるいは特殊文字の周辺にある通常の文字に対する解釈方法に影響します。正規表現パターン文字列には、null byte を含めることができませんが、 '\x00'
のような \number
記法を利用して指定することができます。
繰り返しの修飾子 (*
, +
, ?
, {m,n}
など) は直接入れ子にはできません。
これによって、非貪欲な修飾子の接尾辞 ?
や他の実装での他の修飾子についての曖昧さを回避しています。
繰り返しのある正規表現の外側にさらに繰り返しを適用するには丸括弧が使えます。
例えば、正規表現 (?:a{6})*
は6つの 'a'
の0回以上の繰り返しに適合します。
特殊文字を以下に示します:
'.'
(ドット) デフォルトのモードでは改行以外の任意の文字にマッチします。
DOTALL
フラグが指定されていれば改行も含むすべての文字にマッチします。'^'
(キャレット) 文字列の先頭とマッチします。
MULTILINE
モードでは各改行の直後にマッチします。'$'
文字列の末尾、あるいは文字列の末尾の改行の直前にマッチします。例えば、
foo
は ‘foo’ と ‘foobar’ の両方にマッチします。一方、正規表現foo$
は ‘foo’ だけとマッチします。興味深いことに、'foo1\nfoo2\n'
をfoo.$
で検索した場合、通常のモードでは ‘foo2’ だけにマッチし、MULTILINE
モードでは ‘foo1’ にもマッチします。$
だけで'foo\n'
を検索した場合、2箇所 (内容は空) でマッチします: 1つは、改行の直前で、もう1つは、文字列の最後です。'*'
直前にある RE に作用して、 RE を 0 回以上できるだけ多く繰り返したものにマッチさせるようにします。例えば
ab*
は ‘a’、’ab’、あるいは ‘a’ に任意個数の’b’ を続けたものにマッチします。'+'
直前にある RE に作用して、 RE を、1 回以上繰り返したものにマッチさせるようにします。例えば
ab+
は ‘a’ に一つ以上の ‘b’ が続いたものにマッチし、 ‘a’ 単体にはマッチしません。'?'
直前にある RE に作用して、 RE を 0 回か 1 回繰り返したものにマッチさせるようにします。例えば
ab?
は ‘a’ あるいは ‘ab’ にマッチします。*?
,+?
,??
'*'
、'+'
、'?'
といった修飾子は、すべて 貪欲 (greedy) マッチ、すなわちできるだけ多くのテキストにマッチするようになっています。時にはこの動作が望ましくない場合もあります。例えば正規表現<.*>
を<a> b <c>
にマッチさせると、<a>
だけにマッチするのではなく全文字列にマッチしてしまいます。?
を修飾子の後に追加すると、 非貪欲 (non-greedy) あるいは 最小一致 (minimal) のマッチになり、できるだけ 少ない 文字数のマッチになります。例えば正規表現<.*?>
を使うと<a>
だけにマッチします。{m}
前にある RE の m 回の正確なコピーとマッチすべきであることを指定します;マッチ回数が少なければ、RE 全体ではマッチしません。例えば、
a{6}
は、正確に 6個の'a'
文字とマッチしますが、 5個ではマッチしません。{m,n}
結果の RE は、前にある RE を、 m 回から n 回まで繰り返したもので、できるだけ多く繰り返したものとマッチするように、マッチします。例えば、
a{3,5}
は、3個から 5個の'a'
文字とマッチします。 m を省略するとマッチ回数の下限として0を指定した事になり、 n を省略することは、上限が無限であることを指定します;a{4,}b
はaaaab
や、千個の'a'
文字にb
が続いたものとマッチしますが、aaab
とはマッチしません。コンマは省略できません、そうでないと修飾子が上で述べた形式と混同されてしまうからです。{m,n}?
結果の RE は、前にある RE の m 回から n 回まで繰り返したもので、できるだけ 少なく 繰り返したものとマッチするように、マッチします。これは、前の修飾子の控え目バージョンです。例えば、 6文字文字列
'aaaaaa'
では、a{3,5}
は、5個の'a'
文字とマッチしますが、a{3,5}?
は3個の文字とマッチするだけです。'\'
特殊文字をエスケープする(
'*'
や'?'
等のような文字とのマッチをできるようにする) か、あるいは、特殊シーケンスの合図です; 特殊シーケンスは後で議論します。もしパターンを表現するのに raw string を使用していないのであれば、 Python も、バックスラッシュを文字列リテラルでのエスケープシーケンスとして使っていることを覚えていて下さい。エスケープシーケンスを Python の構文解析器が認識して処理しない場合、そのバックスラッシュとそれに続く文字は、結果の文字列にそのまま含まれます。しかし、Python が結果のシーケンスを認識する場合は、バックスラッシュを 2回繰り返さなければいけません。これは複雑で理解しにくいので、最も簡単な表現以外は、すべて raw string を使うことを強く推奨します。
[]
文字の集合を指定するのに使用します。集合には以下のものが指定できます:
個別に指定できる文字。
[amk]
は'a'
,'m'
,'k'
とマッチします。連続した文字の範囲を、先頭と最後の2文字とその間に
'-'
を挟んだ形で指定できます。[a-z]
はすべての小文字の ASCII 文字とマッチします。[0-5][0-9]
は00
から59
までの、すべての 2 桁の数字とマッチします。[0-9A-Fa-f]
は任意の 16 進数の数字とマッチします。-
が、エスケープされた場合 (例:[a\-z]
)、あるいは先頭か末尾に置かれた場合 (例:[a-]
)、リテラル'-'
とマッチします。集合内では、特殊文字はその意味を失います。
[(+*)]
はリテラル文字'('
'+'
、'*'
、あるいは')'
のいずれかとマッチします。\w
や\S
のような文字クラス (後述) も集合内に指定できますが、それらにマッチする文字はASCII
かLOCALE
のどちらか有効にされているモードに依存します。範囲内にない文字とは、その集合の 補集合 をとることでマッチできます。集合の最初の文字が
'^'
の時、集合に ない 文字すべてとマッチします。[^5]
は'5'
を除くあらゆる文字にマッチします。[^^]
は'^'
を除くあらゆる文字にマッチします。^
は集合の最初の文字でない限り特別の意味を持ちません。集合内でリテラル
']'
をマッチさせるには、その前にバックスラッシュをつけるか、集合の先頭に置きます。[()[\]{}]
と[]()[{}]
はどちらも']'
にマッチします。
'|'
A と B を任意の RE としたとき、
A|B
は A か B のどちらかとマッチする正規表現を作成します。任意個数の RE を、このように'|'
で分離することができます。これはグループ (以下参照) 内部でも同様に使えます。検査対象文字列をスキャンする中で、'|'
で分離された RE は左から右への順に検査されます。一つでも完全にマッチしたパターンがあれば、そのパターン枝が受理されます。つまり、A
がマッチした場合、たとえB
によるマッチが全体としてより長いマッチになったとしても、B
をスキャンしません。言いかえると、'|'
演算子は決して貪欲 (greedy) ではありません。文字通りの'|'
とマッチするには、\|
を使うか、あるいはそれを[|]
のように文字クラス内に入れます。(...)
丸括弧の中にどのような正規表現があってもマッチし、またグループの先頭と末尾を表します;グループの中身は、マッチが実行された後に検索され、後述する
\number
特殊シーケンス付きの文字列内で、後でマッチされます。文字通りの'('
や')'
とマッチするには、\(
あるいは\)
を使うか、それらを文字クラス内に入れます:[(] [)]
。(?...)
これは拡張記法です (
'('
に続く'?'
は他には意味がありません) 。'?'
の後の最初の文字が、この構造の意味とこれ以上のシンタックスがどういうものであるかを決定します。拡張記法は普通新しいグループを作成しません;(?P<name>...)
がこの規則の唯一の例外です。以下に現在サポートされている拡張記法を示します。(?aiLmsux)
(集合
'a'
,'i'
,'L'
,'m'
,'s'
,'u'
,'x'
から1文字以上)。グループは空文字列ともマッチします; 文字は、正規表現全体の対応するフラグre.A
(ASCII 限定マッチ)、re.I
(大文字・小文字を区別しない)、re.L
(ロケール依存)、re.M
(MULTILINE モード)、re.S
(DOTALL モード)、およびre.X
(冗長) を設定します (フラグについては モジュールコンテンツ を参照)。これは、flag 引数をre.compile()
関数に渡すのではなく、そのフラグを正規表現の一部に含めたい場合に役立ちます。(?x)
フラグは、式が構文解析される方法を変更することに注意して下さい。これは式文字列内の最初か、あるいは1つ以上の空白文字の後で使うべきです。もしこのフラグの前に非空白文字があると、その結果は未定義です。(?:...)
正規表現の丸括弧の取り込まないバージョンです。どのような正規表現が丸括弧内にあってもマッチしますが、グループによってマッチされたサブ文字列は、マッチを実行したあと検索されることも、あるいは後でパターンで参照されることも できません 。
(?P<name>...)
正規表現の丸括弧に似ていますが、グループによってマッチした部分文字列はシンボリックグループ名 name によってアクセス可能になります。グループ名は有効な Python 識別子でなければならず、グループ名は 1 個の正規表現内で一意でなければなりません。シンボリックグループは番号付けもされており、番号によるアクセスも可能です。
名前付きグループは 3 つのコンテキストで参照できます。パターンが
(?P<quote>['\"]).*?(?P=quote)
(シングルまたはダブルクオートのどちらかにマッチ) の場合`:グループ “quote” を参照するコンテキスト
参照方法
同一パターンへの参照
(?P=quote)
(そのまま)\1
マッチオブジェクト
m
の処理時m.group('quote')
m.end('quote')
(etc.)
re.sub()
のrepl
引数へ渡される文字列\g<quote>
\g<1>
\1
(?P=name)
名前付きグループへの後方参照です; 既出のグループ名 name にマッチする文字列は何にでもマッチします。
(?#...)
コメントです;括弧の内容は単純に無視されます。
(?=...)
もし
...
が次に続くものとマッチすればマッチしますが、文字列をまったく消費しません。これは先読みアサーション (lookahead assertion) と呼ばれます。例えば、Isaac (?=Asimov)
は、'Isaac '
に'Asimov'
が続く場合だけ、'Isaac '
とマッチします。(?!...)
もし
...
が次に続くものとマッチしなければマッチします。これは否定先読みアサーション (negative lookahead assertion) です。例えば、Isaac (?!Asimov)
は、'Isaac '
に'Asimov'
が続か ない 場合のみマッチします。(?<=...)
文字列内の現在位置の前に、現在位置で終わる
...
とのマッチがあれば、マッチします。これは 後読みアサーション と呼ばれます。(?<=abc)def
はabcdef
にマッチを見つけます。後読みは 3 文字をバックアップし、含まれているパターンとマッチするかどうか検査します。含まれるパターンは、固定長の文字列にのみマッチしなければなりません。すなわち、abc
やa|b
は許されますが、a*
やa{3,4}
は許されません。肯定後読みアサーションで始まるパターンは、検索される文字列の先頭とは決してマッチしないことに注意して下さい; この表現を使用するのは、おそらくmatch()
関数よりsearch()
関数の方が適しています:>>> import re >>> m = re.search('(?<=abc)def', 'abcdef') >>> m.group(0) 'def'
この例ではハイフンに続く単語を探します:
>>> m = re.search('(?<=-)\w+', 'spam-egg') >>> m.group(0) 'egg'
バージョン 3.5 で変更: 固定長のグループ参照をサポートするようになりました。
(?<!...)
文字列内の現在位置の前に
...
とのマッチがない場合に、マッチします。これは 否定後読みアサーション(negative lookbehind assertion) と呼ばれます。肯定後読みアサーションと同様に、含まれるパターンは固定長さの文字列だけにマッチしなければなりません。否定後読みアサーションで始まるパターンは、検索される文字列の先頭とマッチできます。(?(id/name)yes-pattern|no-pattern)
与えられたグループ id あるいは name が存在する場合、
yes-pattern
とのマッチを、存在しない場合no-pattern
とのマッチを試みます。no-pattern
は省略可能です。例えば、(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)
は貧弱な E-mail マッチングパターンで、'<user@host.com>'
や'user@host.com'
にマッチしますが、'<user@host.com'
や'user@host.com>'
とはマッチしません。
特殊シーケンスは '\'
と以下のリストにある文字から構成されます。もしリストにあるのが通常文字でないならば、結果の RE は2番目の文字とマッチします。例えば、 \$
は文字 '$'
とマッチします。
\number
同じ番号のグループの中身とマッチします。グループは1から始まる番号をつけられます。例えば、
(.+) \1
は、'the the'
あるいは'55 55'
とマッチしますが、'thethe'
とはマッチしません(グループの後のスペースに注意して下さい)。この特殊シーケンスは最初の 99 グループのうちの一つとマッチするのに使うことができるだけです。もし number の最初の桁が 0 である、すなわち number が 3 桁の8進数であれば、それはグループのマッチとは解釈されず、 8進数値 number を持つ文字として解釈されます。文字クラスの'['
と']'
の中の数値エスケープは、文字として扱われます。\A
文字列の先頭だけにマッチします。
\b
空文字列とマッチしますが、単語の先頭か末尾の時だけです。単語とは Unicode 英数字 (日本語など英字以外も含む非記号) またはアンダースコアからなるシーケンスで、単語の終わりは空白文字、あるいはアンダースコアを除く記号で表します。
\b
は\w
および\W
の間 (およびその逆) あるいは\w
と文字列の開始/終了との間の境界として定義されています。例えば、r'\bfoo\b'
は'foo'
,'foo.'
,'(foo)'
,'bar foo baz'
にマッチしますが、'foobar'
,'foo3'
にはマッチしません。デフォルトでは、Unicode 英数字 (日本語など英字以外も含む非記号) が使用されますが、これは
ASCII
フラグを使って ASCII に限定できます。文字範囲内では、\b
は Python の文字列リテラルとの互換性のため、後退 (backspace) 文字を表します。\B
空文字列とマッチしますが、それが単語の先頭あるいは末尾に ない 時だけです。
r'py\B'
は'python'
,'py3'
,'py2'
にはマッチしますが、'py'
,'py.'
,'py!'
にはマッチしません。\B
は\b
のちょうど反対で、単語の文字は Unicode 英数字 (日本語など英字以外も含む非記号) またはアンダースコアですが、ASCII
フラグで ASCII に限定できます。\d
- ユニコード (str) パターンに対して:
任意の Unicode 10進数 (Unicode 文字カテゴリ [Nd]) とマッチします。これには
[0-9]
とその他の 10 進数文字が含まれます。ASCII
が使用された場合、[0-9]
のみマッチします。ただし、このフラグは正規表現全体に作用しますので、明示的に[0-9]
と指定する方が良い場合があるかもしれません。- 8bit (bytes) パターンに対して:
任意の 10 進数にマッチします; これは
[0-9]
と等価です。
\D
任意の非 Unicode 10 進数文字にマッチします。これは
\d
の反対です。ASCII
フラグを使用すると[^0-9]
と等価になります。ただし、このフラグは正規表現全体に作用しますので、明示的に[^0-9]
と指定する方が良い場合があるかもしれません。\s
- ユニコード (str) パターンに対して:
任意の空白文字とマッチします。これには
[ \t\n\r\f\v]
およびノーブレークスペースなど、多くの言語におけるタイポグラフィ規則で定義された文字が含まれます。ASCII
フラグを使用すると、[ \t\n\r\f\v]
のみにマッチします。ただし、このフラグは正規表現全体に作用しますので、明示的に[ \t\n\r\f\v]
と指定する方が良い場合があるかもしれません。- 8bit (bytes) パターンに対して:
ASCII 文字セットにおける空白文字とマッチします。これは
[ \t\n\r\f\v]
と等価です。
\S
任意の非空白文字にマッチします。これは
\s
の反対です。ASCII
フラグを使用すると[^ \t\n\r\f\v]
と等価になります。ただし、このフラグは正規表現全体に作用しますので、明示的に[^ \t\n\r\f\v]
と指定する方が良い場合があるかもしれません。\w
- ユニコード (str) パターンに対して:
任意の Unicode 単語文字にマッチします。これにはあらゆる言語で単語の一部になりうる文字、数字、およびアンダースコアが含まれます。
ASCII
フラグを使用すると[a-zA-Z0-9_]
のみにマッチします。ただし、このフラグは正規表現全体に作用しますので、明示的に[a-zA-Z0-9_]
と指定する方が良い場合があるかもしれません。- 8bit (bytes) パターンに対して:
ASCII 文字セットでの英数字とアンダースコアにマッチします。これは
[a-zA-Z0-9_]
と等価です。
\W
任意の非 Unicode 単語文字にマッチします。これは
\w
の反対です。ASCII
フラグを使用した場合、[^a-zA-Z0-9_]
と等価になります。ただし、このフラグは正規表現全体に作用しますので、明示的に[^a-zA-Z0-9_]
と指定する方が良い場合があるかもしれません。\Z
文字列の末尾とのみマッチします。
Python 文字列リテラルによってサポートされている標準エスケープのほとんども、正規表現パーザによって認識されます:
\a \b \f \n
\r \t \u \U
\v \x \\
(\b
は単語の境界を表し、文字クラス内でのみ後退 (backspace) 文字を指すことに注意してください)
'\u'
および '\U'
エスケープシーケンスは Unicode パターン内でのみ認識されます。バイト列では特殊文字として扱われません。
8 進数エスケープは限られた形式で表します。最初の桁が 0 か、あるいは 3 桁の 8 進数ならば、8 進数エスケープとみなされます。それ以外の場合はグループ参照になります。文字列リテラルに関しては、8 進数エスケープはほとんどの場合 3 桁長になります。
バージョン 3.3 で変更: '\u'
と '\U'
エスケープシーケンスが追加されました。
バージョン 3.5 で非推奨、バージョン 3.6 で削除予定。’’ および ASCII 文字からなる未知のエスケープは、非推奨の警告を送出し、Python 3.6 では禁止されます。
参考
- Mastering Regular Expressions 詳説正規表現
Jeffrey Friedl 著、O’Reilly 刊の正規表現に関する本です。この本の第2版ではPyhonについては触れていませんが、良い正規表現パターンの書き方を非常にくわしく説明しています。
6.2.2. モジュールコンテンツ¶
このモジュールはいくつかの関数、定数、例外を定義します。この関数の一部はコンパイルした正規表現の完全版メソッドを簡略化したバージョンです。簡単なアプリケーションを除くほとんどで、コンパイルされた形式が用いられるのが普通です。
-
re.
compile
(pattern, flags=0)¶ 正規表現パターンを正規表現オブジェクトにコンパイルします。このオブジェクトは、以下で述べる
match()
とsearch()
メソッドを使って、マッチングに使うことができます。式の動作は、 flags の値を指定することで加減することができます。値は以下の変数を、ビットごとの OR (
|
演算子)を使って組み合わせることができます。シーケンス
prog = re.compile(pattern) result = prog.match(string)
は、以下と同等です
result = re.match(pattern, string)
ただし、その式を一つのプログラムで何回も使う場合には、
re.compile()
を使ってその結果の正規表現オブジェクトを再利用した方がより効率的です。注釈
re.compile()
に渡されてコンパイルされた最新のパターンと、モジュールレベルのマッチング関数はキャッシュされるので、一度に少しの正規表現しか使わないプログラムは正規表現のコンパイルについて心配する必要はありません。
-
re.
A
¶ -
re.
ASCII
¶ \w
、\W
、\b
、\B
、\d
、\D
、\s
、および\S
において、ASCII 文字のみでマッチングを行います。これは Unicode パターンでのみ意味があり、バイト列パターンでは無視されます。後方互換性のため、
re.U
フラグ (およびそれと同義のre.UNICODE
と埋め込みで使用する(?u)
) はまだ存在していますが、文字列のマッチのデフォルトが Unicode になった Python 3 では冗長です (そして Unicode マッチングではバイト列は扱えません)。
-
re.
DEBUG
¶ コンパイルした表現に関するデバッグ情報を出力します。
-
re.
I
¶ -
re.
IGNORECASE
¶ 英大文字・小文字を区別せずにマッチングを行います。
[A-Z]
のような表現は小文字ともマッチします。これは現在のロケールの影響を受けず、Unicode 文字に対しても動作します。
-
re.
L
¶ -
re.
LOCALE
¶ \w
、\W
、\b
、\B
、\s
、および\S
において、現在のロケールに従ったマッチングを行います。ロケールのメカニズムは非常に信頼性に欠けるため、このフラグの使用は推奨されません。このフラグは、一度に一つの “文化” を扱うことしかできません。代わりに、Python 3 において Unicode (文字列) パターンのデフォルトである、Unicode マッチングを使用してください。このフラグは、バイト列でのみ意味を持ちます。バージョン 3.5 で非推奨、バージョン 3.6 で削除予定。文字列パターンまたは re.ASCII と re.LOCALE を併用することは推奨されません。
-
re.
M
¶ -
re.
MULTILINE
¶ 指定されると、パターン文字
'^'
は、文字列の先頭および各行の先頭(各改行の直後)とマッチします;そしてパターン文字'$'
は文字列の末尾および各行の末尾 (改行の直前) とマッチします。デフォルトでは、'^'
は、文字列の先頭とだけマッチし、'$'
は、文字列の末尾および文字列の末尾の改行の直前(がもしあれば)とマッチします。
-
re.
X
¶ -
re.
VERBOSE
¶ このフラグによって、より良い、読みやすい正規表現を書くことができます。パターンの論理的なセクションを視覚的に区切り、コメントも入れることができます。パターン内の空白は、文字クラス内にあるかエスケープされていないバックスラッシュが前にある場合以外は無視されます。文字クラス内にもなく、エスケープされていないバックスラッシュが前にもない
'#'
が行内にある場合は、そのような'#'
のうち一番左にあるものからその行の末尾までが無視されます。つまり、数字にマッチする下記のふたつの正規表現オブジェクトは、機能的に等価です。:
a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X) b = re.compile(r"\d+\.\d*")
-
re.
search
(pattern, string, flags=0)¶ string を走査し、正規表現 pattern がマッチする最初の場所を探して、対応する match オブジェクト を返します。文字列内にパターンにマッチする場所が無い場合は
None
を返します; これは文字列内のある場所で長さが 0 のマッチが見つかった場合とは異なることに注意してください。
-
re.
match
(pattern, string, flags=0)¶ もし string の先頭で 0 個以上の文字が正規表現 pattern とマッチすれば、対応する マッチオブジェクト インスタンスを返します。文字列がパターンとマッチしなければ、
None
を返します; これは長さゼロのマッチとは異なることに注意して下さい。MULTILINE
モードであっても、re.match()
は文字列の先頭のみにマッチし、各行の先頭にはマッチしないことに注意してください。string 内のどこでもマッチできるようにするには、代わりに
search()
を使ってください (search() vs. match() も参照してください)。
-
re.
fullmatch
(pattern, string, flags=0)¶ string 全体が正規表現 pattern にマッチした場合、対応する match オブジェクト を返します。文字列にパターンにマッチする場所が無い場合は
None
を返します; これは長さが 0 のマッチとは違うということを注意しておきます。バージョン 3.4 で追加.
-
re.
split
(pattern, string, maxsplit=0, flags=0)¶ string を、pattern があるたびに分割します。キャプチャグループの丸括弧が pattern で使われていれば、パターン内のすべてのグループのテキストも結果のリストの一部として返されます。maxsplit がゼロでなければ、最大 maxsplit 個の分割が発生し、残りはリストの最終要素として返されます。
>>> re.split('\W+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split('(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split('\W+', 'Words, words, words.', 1) ['Words', 'words, words.'] >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE) ['0', '3', '9']
もし、捕捉するグループが分割パターンに含まれ、それが文字列の先頭にあるならば、分割結果は、空文字列から始まります。文字列最後においても同様です。
>>> re.split('(\W+)', '...words, words...') ['', '...', 'words', ', ', 'words', '...', '']
その場合、常に、分割要素が、分割結果のリストの相対的なインデックスに現れます。
注釈
現在、
split()
は空のパターンマッチでは文字列を分割しないことに注意してください。例えば、次のようになります。>>> re.split('x*', 'axbc') ['a', 'bc']
'x*'
は ‘a’ の前、 ‘b’ と ‘c’ との間、 ‘c’ の後の 0 個の ‘x’ にもマッチしますが、現在これらのマッチは無視されます。正しい動作 (空のマッチでも文字列を分割し、['', 'a', 'b', 'c', '']
を返す) は、Python の将来のバージョンで実装されます。これは、後方互換生のない変更であるため、移行期間中はFutureWarning
が送出されます。空の文字列のみとマッチするパターンは、現在文字列を全く分割しません。これは望ましい動作ではないため、Python 3.5 から
ValueError
が送出されます:>>> re.split("^$", "foo\n\nbar\n", flags=re.M) Traceback (most recent call last): File "<stdin>", line 1, in <module> ... ValueError: split() requires a non-empty pattern match.
バージョン 3.1 で変更: オプションの flags 引数が追加されました。
バージョン 3.5 で変更: 空の文字列にマッチするパターンによって分割しようとすると、警告が送出されるようになりました。空の文字列のみとマッチするパターンは拒否されるようになりました。
-
re.
findall
(pattern, string, flags=0)¶ pattern の string へのマッチのうち、重複しない全てのマッチを文字列のリストとして返します。 string は左から右へと走査され、マッチは見つかった順番で返されます。パターン中に何らかのグループがある場合、グループのリストを返します。グループが複数定義されていた場合、タプルのリストになります。他のマッチの開始部分に接触しないかぎり、空のマッチも結果に含められます。
-
re.
finditer
(pattern, string, flags=0)¶ string 内の RE pattern の重複しないマッチの マッチオブジェクト を yield する イテレータ を返します。string は左から右へと走査され、マッチは見つかった順番で返されます。他のマッチの開始部分に接触しないかぎり、空のマッチも結果に含められます。
-
re.
sub
(pattern, repl, string, count=0, flags=0)¶ string 内で、pattern のマッチを repl で置き換えた文字列を返します。パターンが重複している場合は、一番左のマッチのみを置き換えます。パターンが見つからなければ string を変更せずに返します。repl は文字列でも関数でも構いません; 文字列の場合、バックスラッシュエスケープは処理されます。すなわち、
\n
は単一の改行文字に変換され、\r
はキャリッジリターンに変換されます。\&
のような未知のエスケープは処理されず、そのままにされます。\6
のような後方参照は、パターンのグループ 6 とマッチした部分文字列で置換されます。例えば:>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):', ... r'static PyObject*\npy_\1(void)\n{', ... 'def myfunc():') 'static PyObject*\npy_myfunc(void)\n{'
もし repl が関数であれば、重複しない pattern が発生するたびにその関数が呼ばれます。この関数は一つのマッチオブジェクト引数を取り、置換文字列を返します。例えば:
>>> def dashrepl(matchobj): ... if matchobj.group(0) == '-': return ' ' ... else: return '-' >>> re.sub('-{1,2}', dashrepl, 'pro----gram-files') 'pro--gram files' >>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE) 'Baked Beans & Spam'
パターンは、文字列でも RE オブジェクトでも構いません。
省略可能な引数 count は、置換されるパターンの出現回数の最大値です; count は非負の整数でなければなりません。もし省略されるかゼロであれば、出現したものがすべて置換されます。パターンのマッチが空であれば、以前のマッチと隣合わせでない時だけ置換されますので、
sub('x*', '-', 'abc')
は'-a-b-c-'
を返します。文字列タイプ repl 引数では、上で述べた文字エスケープや後方参照の他に、
\g<name>
は、(?P<name>...)
シンタックスで定義されたname
グループによるマッチ部分文字列を使用することになりますし、\g<number>
は対応するグループ番号への参照となります;\g<2>
はつまり\2
と等価ですが、\g<2>0
のような置換においても曖昧になりません。\20
は、グループ 20への参照として解釈され、グループ 2 にリテラル文字'0'
が続いたものへの参照としては解釈されないかもしれません。後方参照\g<0>
は、RE とマッチするサブ文字列全体を置き換えます。バージョン 3.1 で変更: オプションの flags 引数が追加されました。
バージョン 3.5 で変更: マッチしないグループは空文字列に置き換えられます。
バージョン 3.5 で非推奨、バージョン 3.6 で削除予定。’’ および ASCII 文字からなる未知のエスケープは、非推奨の警告を送出し、Python 3.6 では禁止されます。
-
re.
subn
(pattern, repl, string, count=0, flags=0)¶ sub()
と同じ操作を行いますが、タプル(new_string、 number_of_subs_made)
を返します。バージョン 3.1 で変更: オプションの flags 引数が追加されました。
バージョン 3.5 で変更: マッチしないグループは空文字列に置き換えられます。
-
re.
escape
(pattern)¶ Escape all the characters in pattern except ASCII letters, numbers and
'_'
. This is useful if you want to match an arbitrary literal string that may have regular expression metacharacters in it. For example:>>> print(re.escape('python.exe')) python\.exe >>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:" >>> print('[%s]+' % re.escape(legal_chars)) [abcdefghijklmnopqrstuvwxyz0123456789\!\#\$\%\&\'\*\+\-\.\^_\`\|\~\:]+ >>> operators = ['+', '-', '*', '/', '**'] >>> print('|'.join(map(re.escape, sorted(operators, reverse=True)))) \/|\-|\+|\*\*|\*
バージョン 3.3 で変更:
'_'
文字がエスケープされなくなりました。
-
re.
purge
()¶ 正規表現キャッシュをクリアします。
-
exception
re.
error
(msg, pattern=None, pos=None)¶ これらの関数のいずれかに渡された文字列が有効な正規表現ではない (例: 括弧が対になっていない) 場合、またはコンパイル時やマッチング時になんらかのエラーが発生した場合に発生する例外です。文字列にパターンとマッチする部分がなくても、それはエラーではありません。エラーインスタンスには、次のような追加の属性があります。
-
msg
¶ フォーマットされていないエラーメッセージです。
-
pattern
¶ 正規表現のパターンです。
-
pos
¶ pattern のコンパイルに失敗した場所のインデックスです。
-
lineno
¶ pos に対応する行です。
-
colno
¶ pos に対応する列です。
バージョン 3.5 で変更: 追加の属性が追加されました。
-
6.2.3. 正規表現オブジェクト¶
コンパイル済み正規表現オブジェクトは以下のメソッドと属性をサポートします:
-
regex.
search
(string[, pos[, endpos]])¶ string を走査し、正規表現がマッチする最初の場所を探して、対応する match オブジェクト を返します。文字列内にパターンにマッチする場所がない場合は
None
を返します; これは文字列内のある場所で長さが 0 のマッチが見つかった場合とは異なることに注意してください。省略可能な、2つ目の引数 pos は、 string のどこから探し始めるかを指定する index で、デフォルトでは 0 です。これは、文字列をスライスしてから検索するのと、完全には同じではありません。パターン文字
'^'
は本当の文字列の先頭と、改行の直後にマッチしますが、検索を開始する index がマッチするとは限りません。省略可能な引数 endpos は文字列の検索範囲を制限します。これは文字列の長さが endpos 文字だった場合と同じとみなし、pos から
endpos - 1
の範囲の文字に対してマッチを探します。endpos が pos よりも小さい場合は、マッチは見つかりません。それ以外の場合は、rx がコンパイルされた正規表現として、rx.search(string, 0, 50)
はrx.search(string[:50], 0)
と等価です。>>> pattern = re.compile("d") >>> pattern.search("dog") # Match at index 0 <_sre.SRE_Match object; span=(0, 1), match='d'> >>> pattern.search("dog", 1) # No match; search doesn't include the "d"
-
regex.
match
(string[, pos[, endpos]])¶ string の 先頭の 0 個以上の文字がこの正規表現とマッチする場合、対応する マッチオブジェクト を返します。文字列がパタンーとマッチしない場合、
None
を返します。これは長さゼロのマッチとは異なることに注意してください。省略可能な引数の pos と endpos は、
search()
メソッドの引数と同じ意味を持ちます。>>> pattern = re.compile("o") >>> pattern.match("dog") # No match as "o" is not at the start of "dog". >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog". <_sre.SRE_Match object; span=(1, 2), match='o'>
string 内のどこでもマッチできるようにするには、代わりに
search()
を使ってください (search() vs. match() も参照してください)。
-
regex.
fullmatch
(string[, pos[, endpos]])¶ string 全体がこの正規表現にマッチした場合、対応する match オブジェクト を返します。文字列にパターンにマッチする場所が無い場合は
None
を返します; これは長さが 0 のマッチとは違うということを注意しておきます。省略可能な引数の pos と endpos は、
search()
メソッドの引数と同じ意味を持ちます。>>> pattern = re.compile("o[gh]") >>> pattern.fullmatch("dog") # No match as "o" is not at the start of "dog". >>> pattern.fullmatch("ogre") # No match as not the full string matches. >>> pattern.fullmatch("doggie", 1, 3) # Matches within given limits. <_sre.SRE_Match object; span=(1, 3), match='og'>
バージョン 3.4 で追加.
-
regex.
split
(string, maxsplit=0)¶ split()
関数と同様で、コンパイルしたパターンを使います。ただし、match()
と同じように、省略可能な pos, endpos 引数で検索範囲を指定することができます。
-
regex.
findall
(string[, pos[, endpos]])¶ findall()
関数と同様で、コンパイルしたパターンを使います。ただし、match()
と同じように、省略可能な pos, endpos 引数で検索範囲を指定することができます。
-
regex.
finditer
(string[, pos[, endpos]])¶ finditer()
関数と同様で、コンパイルしたパターンを使います。ただし、match()
と同じように、省略可能な pos, endpos 引数で検索範囲を指定することができます。
-
regex.
flags
¶ 正規表現のマッチングフラグです。これは
compile()
で指定されたフラグ、パターン内の(?...)
インラインフラグ、およびパターンが Unicode 文字列だった時のUNICODE
のような暗黙のフラグとの組み合わせになりなす。
-
regex.
groups
¶ パターンにあるキャプチャグループの数です。
-
regex.
groupindex
¶ (?P<id>)
で定義された任意の記号グループ名の、グループ番号への辞書マッピングです。もし記号グループがパターン内で何も使われていなければ、辞書は空です。
-
regex.
pattern
¶ RE オブジェクトがコンパイルされたときに使用された元のパターン文字列です。
6.2.4. match オブジェクト¶
マッチオブジェクトは常にブール値 True
を持ちます。 match()
と search()
はマッチしなかった場合に None
を返すので、単純な if
ステートメントによってマッチしたかどうかをテストできます:
match = re.search(pattern, string)
if match:
process(match)
マッチオブジェクトは以下のメソッドと属性をサポートしています:
-
match.
expand
(template)¶ テンプレート文字列 template に対し、
sub()
メソッドがするようなバックスラッシュ置換をして得られる文字列を返します。\n
のようなエスケープは適切な文字に変換され、数値の後方参照 (\1
,\2
) と名前付きの後方参照 (\g<1>
,\g<name>
) は、対応するグループの内容で置き換えられます。バージョン 3.5 で変更: マッチしないグループは空文字列に置き換えられます。
-
match.
group
([group1, ...])¶ マッチした1個以上のサブグループを返します。もし引数で一つであれば、その結果は一つの文字列です。複数の引数があれば、その結果は、引数ごとに一項目を持つタプルです。引数がなければ、 group1 はデフォールトでゼロです(マッチしたものすべてが返されます)。もし groupN 引数がゼロであれば、対応する戻り値は、マッチする文字列全体です。もしそれが範囲 [1..99] 内であれば、それは、対応する丸括弧つきグループとマッチする文字列です。もしグループ番号が負であるか、あるいはパターンで定義されたグループの数より大きければ、
IndexError
例外が発生します。グループがマッチしなかったパターンの一部に含まれていれば、対応する結果はNone
です。グループが、複数回マッチしたパターンの一部に含まれていれば、最後のマッチが返されます。>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") >>> m.group(0) # The entire match 'Isaac Newton' >>> m.group(1) # The first parenthesized subgroup. 'Isaac' >>> m.group(2) # The second parenthesized subgroup. 'Newton' >>> m.group(1, 2) # Multiple arguments give us a tuple. ('Isaac', 'Newton')
もし正規表現が
(?P<name>...)
シンタックスを使うならば、 groupN 引数は、それらのグループ名によってグループを識別する文字列であっても構いません。もし文字列引数がパターンのグループ名として使われていないものであれば、IndexError
例外が発生します。適度に複雑な例題:
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.group('first_name') 'Malcolm' >>> m.group('last_name') 'Reynolds'
名前の付けられたグループは、そのインデックスによっても参照できます。
>>> m.group(1) 'Malcolm' >>> m.group(2) 'Reynolds'
もし、グループが複数回マッチする場合、最後のマッチだけが利用可能となります。
>>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times. >>> m.group(1) # Returns only the last match. 'c3'
-
match.
groups
(default=None)¶ パターンにマッチしたすべてのサブグループを含む、パターン内で指定されたグループ数分の要素を持つタプルを返します。引数 default は、マッチに加わらなかったグループ用に使われ、デフォルトでは
None
です。例えば:
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632") >>> m.groups() ('24', '1632')
もし、整数部にのみ着目し、あとの部分をオプションとした場合、マッチの中に現れないグループがあるかも知れません。それらのグループは、 default 引数が与えられていない場合、デフォルトでは
None
になります。>>> m = re.match(r"(\d+)\.?(\d+)?", "24") >>> m.groups() # Second group defaults to None. ('24', None) >>> m.groups('0') # Now, the second group defaults to '0'. ('24', '0')
-
match.
groupdict
(default=None)¶ マッチの、すべての 名前つきの サブグループを含む、サブグループ名でキー付けされた辞書を返します。 default 引数はマッチに加わらなかったグループに使われ、デフォールトでは
None
です。例えば、>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.groupdict() {'first_name': 'Malcolm', 'last_name': 'Reynolds'}
-
match.
start
([group])¶ -
match.
end
([group])¶ group とマッチした部分文字列の先頭と末尾のインデックスを返します。 group は、デフォルトでは(マッチした部分文字列全体を意味する)ゼロです。 group が存在してもマッチに寄与しなかった場合は、
-1
を返します。マッチオブジェクト m および、マッチに寄与しなかったグループ g があって、グループ g とマッチしたサブ文字列 (m.group(g)
と同じ意味ですが ) は:m.string[m.start(g):m.end(g)]
です。もし group が空文字列とマッチすれば、
m.start(group)
がm.end(group)
と等しくなることに注意して下さい。例えば、m = re.search('b(c?)', 'cba')
とすると、m.start(0)
は 1 で、m.end(0)
は 2 であり、m.start(1)
とm.end(1)
はともに 2 であり、m.start(2)
はIndexError
例外を発生します。例として、電子メールのアドレスから remove_this を取り除く場合を示します。
>>> email = "tony@tiremove_thisger.net" >>> m = re.search("remove_this", email) >>> email[:m.start()] + email[m.end():] 'tony@tiger.net'
-
match.
span
([group])¶ マッチ m について、大きさ2のタプル
(m.start(group), m.end(group))
を返します。もし group がマッチに寄与しなければ、(-1, -1)
になります。また group はデフォルトでゼロです。
-
match.
pos
¶ 正規表現オブジェクト の
search()
かmatch()
に渡された pos の値です。これは RE エンジンがマッチを探し始める位置の文字列のインデックスです。
-
match.
endpos
¶ 正規表現オブジェクト の
search()
かmatch()
に渡された endpos の値です。これは RE エンジンがそれ以上は進まない位置の文字列のインデックスです。
-
match.
lastindex
¶ 最後にマッチした取り込みグループの整数インデックスです。もしどのグループも全くマッチしなければ
None
です。例えば、(a)b
,((a)(b))
や((ab))
といった表現が'ab'
に適用された場合、lastindex == 1
となり、同じ文字列に(a)(b)
が適用された場合にはlastindex == 2
となるでしょう。
-
match.
lastgroup
¶ 最後にマッチした取り込みグループの名前です。もしグループに名前がないか、あるいはどのグループも全くマッチしなければ
None
です。
6.2.5. 正規表現の例¶
6.2.5.1. ペアの確認¶
この例では、マッチオブジェクトの表示を少し美しくするために、下記の補助関数を使用します :
def displaymatch(match):
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
あなたがポーカープログラムを書いているとします。プレイヤーの持ち札はそれぞれの文字が1枚のカードを意味する5文字の文字列によって表現されます。 “a” はエース、 “k” はキング、 “q” はクイーン、 “j” はジャック、 “t” は10、そして “2” から “9” はそれぞれの数字のカードを表します。
与えられた文字列が持ち札として有効かどうかの確認は、次のようにして行えます。
>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q")) # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e")) # Invalid.
>>> displaymatch(valid.match("akt")) # Invalid.
>>> displaymatch(valid.match("727ak")) # Valid.
"<Match: '727ak', groups=()>"
最後の持ち札 "727ak"
は、ペアを含んでいます。言い換えると同じ値のカードが2枚あります。これを正規表現にマッチさせるために、後方参照を使う場合もあります :
>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak")) # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak")) # No pairs.
>>> displaymatch(pair.match("354aa")) # Pair of aces.
"<Match: '354aa', groups=('a',)>"
どのカードのペアになっているかを調べるには、以下のようにマッチオブジェクトの group()
メソッドを使います:
>>> pair.match("717ak").group(1)
'7'
# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'
>>> pair.match("354aa").group(1)
'a'
6.2.5.2. scanf() をシミュレートする¶
Python には現在のところ、 scanf()
に相当するものがありません。正規表現は、 scanf()
のフォーマット文字列よりも、一般的により強力であり、また冗長でもあります。以下の表に、 scanf()
のフォーマットトークンと正規表現の大体同等な対応付けを示します。
|
正規表現 |
---|---|
%c |
. |
%5c |
.{5} |
%d |
[-+]?\d+ |
%e , %E , %f , %g |
[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)? |
%i |
[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+) |
%o |
[-+]?[0-7]+ |
%s |
\S+ |
%u |
\d+ |
%x , %X |
[-+]?(0[xX])?[\dA-Fa-f]+ |
以下のような文字列からファイル名と数値を抽出することを考えます
/usr/sbin/sendmail - 0 errors, 4 warnings
scanf()
フォーマットは次のように使います
%s - %d errors, %d warnings
同等な正規表現はこのようなものとなります
(\S+) - (\d+) errors, (\d+) warnings
6.2.5.3. search() vs. match()¶
Python は正規表現ベースの 2 個の基本的な関数、文字列の先頭でのみのマッチを確認する re.match()
および、文字列内の位置にかかわらずマッチを確認する re.search()
(Perl でのデフォルトの挙動) を提供しています。
例えば:
>>> re.match("c", "abcdef") # No match
>>> re.search("c", "abcdef") # Match
<_sre.SRE_Match object; span=(2, 3), match='c'>
'^'
で始まる正規表現は、 search()
において、マッチを文字列の先頭からに制限するために使用します:
>>> re.match("c", "abcdef") # No match
>>> re.search("^c", "abcdef") # No match
>>> re.search("^a", "abcdef") # Match
<_sre.SRE_Match object; span=(0, 1), match='a'>
ただし、 MULTILINE
モードの match()
では文字列の先頭にのみマッチするのに対し、正規表現に '^'
を使った search()
では各行の先頭にもマッチします。
>>> re.match('X', 'A\nB\nX', re.MULTILINE) # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE) # Match
<_sre.SRE_Match object; span=(4, 5), match='X'>
6.2.5.4. 電話帳の作成¶
split()
は文字列を与えられたパターンで分割し、リストにして返します。下記の、電話帳作成の例のように、このメソッドはテキストデータを読みやすくしたり、 Python で編集したりしやすくする際に、非常に役に立ちます。
最初に、入力を示します。通常、これはファイルからの入力になるでしょう。ここでは、3重引用符の書式とします :
>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""
個々の記録は、1つ以上の改行で区切られています。まずは、文字列から空行を除き、記録ごとのリストに変換しましょう。
>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']
そして、各記録を、名、姓、電話番号、そして、住所に分割してリストにします。分割のためのパターンに使っている空白文字が、住所には含まれるため、 split()
の maxsplit
引数を使います。 :
>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]
パターン、 :?
は姓に続くコロンにマッチします。そのため、コロンは分割結果のリストには現れません。 maxsplit
を 4
にすれば、ハウスナンバーと、ストリート名を分割することができます。 :
>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]
6.2.5.5. テキストの秘匿¶
sub()
はパターンにマッチした部分を文字列や関数の返り値で置き換えます。この例では、”秘匿” する文字列に、関数と共に sub()
を適用する例を示します。言い換えると、最初と最後の文字を除く、単語中の文字の位置をランダム化します。
>>> def repl(m):
... inner_word = list(m.group(2))
... random.shuffle(inner_word)
... return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'
6.2.5.6. 全ての副詞を見つける¶
findall()
はパターンにマッチする 全てに マッチします。 search()
がそうであるように、最初のものだけに、ではありません。例えば、なにかの文章の全ての副詞を見つけたいとき、下記のように findall()
を使います。 :
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']
6.2.5.7. 全ての副詞と、その位置を見つける¶
もし、パターンにマッチするものについて、マッチしたテキスト以上の情報を得たいと考えた時、文字列ではなく マッチオブジェクト を返す finditer()
が便利です。以下に例を示すように、なにかの文章のすべての副詞と、 その位置を 調べたい時、以下のように finditer()
を使います:
>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
... print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly
6.2.5.8. Raw String記法¶
Raw string記法 (r"text"
) により、バックスラッシュ ('\'
) を個々にバックスラッシュでエスケープすることなしに、正規表現を正常な状態に保ちます。例えば、下記の2つのコードは機能的に等価です。 :
>>> re.match(r"\W(.)\1\W", " ff ")
<_sre.SRE_Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<_sre.SRE_Match object; span=(0, 4), match=' ff '>
文字通りのバックスラッシュにマッチさせたいなら、正規表現中ではエスケープする必要があります。 Raw string記法では、 r"\\"
ということになります。 Raw string記法を用いない場合、 "\\\\"
としなくてはなりません。下記のコードは機能的に等価です。 :
>>> re.match(r"\\", r"\\")
<_sre.SRE_Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<_sre.SRE_Match object; span=(0, 1), match='\\'>
6.2.5.9. トークナイザを書く¶
トークナイザやスキャナ は文字列を解析し、文字のグループにカテゴリ分けします。これはコンパイラやインタプリタを作成する最初の一歩として役立ちます。
テキストのカテゴリは正規表現で指定されます。技術的には、それらを一つのマスター正規表現に結合し、連続したマッチをループさせます:
import collections
import re
Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column'])
def tokenize(code):
keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
token_specification = [
('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number
('ASSIGN', r':='), # Assignment operator
('END', r';'), # Statement terminator
('ID', r'[A-Za-z]+'), # Identifiers
('OP', r'[+\-*/]'), # Arithmetic operators
('NEWLINE', r'\n'), # Line endings
('SKIP', r'[ \t]+'), # Skip over spaces and tabs
('MISMATCH',r'.'), # Any other character
]
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
line_num = 1
line_start = 0
for mo in re.finditer(tok_regex, code):
kind = mo.lastgroup
value = mo.group(kind)
if kind == 'NEWLINE':
line_start = mo.end()
line_num += 1
elif kind == 'SKIP':
pass
elif kind == 'MISMATCH':
raise RuntimeError('%r unexpected on line %d' % (value, line_num))
else:
if kind == 'ID' and value in keywords:
kind = value
column = mo.start() - line_start
yield Token(kind, value, line_num, column)
statements = '''
IF quantity THEN
total := total + price * quantity;
tax := price * 0.05;
ENDIF;
'''
for token in tokenize(statements):
print(token)
トークナイザは以下の出力を作成します:
Token(typ='IF', value='IF', line=2, column=4)
Token(typ='ID', value='quantity', line=2, column=7)
Token(typ='THEN', value='THEN', line=2, column=16)
Token(typ='ID', value='total', line=3, column=8)
Token(typ='ASSIGN', value=':=', line=3, column=14)
Token(typ='ID', value='total', line=3, column=17)
Token(typ='OP', value='+', line=3, column=23)
Token(typ='ID', value='price', line=3, column=25)
Token(typ='OP', value='*', line=3, column=31)
Token(typ='ID', value='quantity', line=3, column=33)
Token(typ='END', value=';', line=3, column=41)
Token(typ='ID', value='tax', line=4, column=8)
Token(typ='ASSIGN', value=':=', line=4, column=12)
Token(typ='ID', value='price', line=4, column=15)
Token(typ='OP', value='*', line=4, column=21)
Token(typ='NUMBER', value='0.05', line=4, column=23)
Token(typ='END', value=';', line=4, column=27)
Token(typ='ENDIF', value='ENDIF', line=5, column=4)
Token(typ='END', value=';', line=5, column=9)