re
--- 正規表現操作¶
ソースコード: Lib/re.py
このモジュールは Perl に見られる正規表現マッチング操作と同様のものを提供します。
パターンおよび検索される文字列には、Unicode 文字列 (str
) や 8 ビット文字列 (bytes
) を使います。ただし、Unicode 文字列と 8 ビット文字列の混在はできません。つまり、Unicode 文字列にバイト列のパターンでマッチングしたり、その逆はできません。同様に、置換時の置換文字列はパターンおよび検索文字列の両方と同じ型でなくてはなりません。
正規表現では、特殊な形式を表すためや、特殊文字をその特殊な意味を発動させず使うために、バックスラッシュ文字 ('\'
) を使います。こうしたバックスラッシュの使い方は、 Python の文字列リテラルにおける同じ文字の使い方と衝突します。例えば、リテラルのバックスラッシュにマッチさせるには、パターン文字列として '\\\\'
と書かなければなりません。なぜなら、正規表現は \\
でなければならないうえ、それぞれのバックスラッシュは標準の Python 文字列リテラルで \\
と表現せねばならないからです。
これを解決するには、正規表現パターンに Python の raw 文字列記法を使います。 'r'
を前置した文字列リテラル内ではバックスラッシュが特別扱いされません。従って "\n"
が改行一文字からなる文字列であるのに対して、 r"\n"
は '\'
と 'n'
の二文字からなる文字列です。通常、 Python コード中では、パターンをこの raw 文字列記法を使って表現します。
重要なこととして、大抵の正規表現操作は、モジュールレベルの関数としても、 コンパイル済み正規表現 のメソッドとしても利用できます。関数は正規表現オブジェクトを前もってコンパイルする必要がない近道ですが、微調整のための変数が減ります。
正規表現のシンタックス¶
正規表現 (または RE) は、その表現にマッチ (match) する文字列の集合を指定します。このモジュールの関数を使えば、ある文字列が与えられた正規表現にマッチするか (または、与えられた正規表現がある文字列にマッチするか、と言い換えても同じことになります) を検査できます。
正規表現を連結することで新しい正規表現を作れます。A と B がともに正規表現であれば AB も正規表現です。一般的に、ある文字列 p が A にマッチし、別の文字列 q が B にマッチするなら、文字列 pq は AB にマッチします。ただし、 A または B に優先度の低い演算が含まれる場合や、 A と B との間に境界条件がある場合や、番号付けされたグループ参照をしている場合、を除きます。こうして、ここで述べるような簡単な基本表現から、複雑な表現を容易に構築できます。正規表現に関する理論と実装の詳細については Friedl 本 [Frie09] か、コンパイラの構築に関するテキストを参照してください。
以下で正規表現の形式を簡単に説明します。詳細な情報ややさしい説明は、 正規表現 HOWTO を参照してください。
正規表現には、特殊文字と通常文字の両方を含められます。 'A'
、 'a'
、または '0'
のようなほとんどの通常文字は、最も単純な正規表現です。これは単純に、その文字自体にマッチします。通常文字は連結できるので、 last
は文字列 'last'
にマッチします。 (この節では以降、正規表現は一般にクオートを使わず この特殊スタイルで
表記し、マッチ対象の文字列は、 'シングルクオートで括って'
表記します。)
'|'
や '('
といったいくつかの文字は特殊です。特殊文字は通常文字の種別を表したり、周辺の通常文字に対する解釈方法に影響します。
繰り返しの修飾子 (*
、 +
、 ?
、 {m,n}
など) は直接入れ子にはできません。これは、非貪欲な修飾子の接尾辞 ?
や他の実装での他の修飾子との曖昧さを回避します。内側で繰り返したものをさらに繰り返すには、丸括弧が使えます。例えば、正規表現 (?:a{6})*
は 6 の倍数個の 'a'
文字にマッチします。
特殊文字を以下に示します:
.
(ドット) デフォルトのモードでは改行以外の任意の文字にマッチします。
DOTALL
フラグが指定されていれば改行も含む全ての文字にマッチします。
^
(キャレット) 文字列の先頭にマッチし、
MULTILINE
モードでは各改行の直後にもマッチします。
$
文字列の末尾、あるいは文字列の末尾の改行の直前にマッチし、
MULTILINE
モードでは改行の前にもマッチします。foo
は 'foo' と 'foobar' の両方にマッチしますが、正規表現foo$
は 'foo' だけにマッチします。興味深いことに、'foo1\nfoo2\n'
をfoo.$
で検索した場合、通常は 'foo2' だけにマッチしますが、MULTILINE
モードでは 'foo1' にもマッチします。$
だけで'foo\n'
を検索した場合、2 つの (空の) マッチを見つけます: 1つは改行の直前で、もう1つは文字列の末尾です。
*
直前の正規表現を 0 回以上、できるだけ多く繰り返したものにマッチさせる結果の正規表現にします。例えば
ab*
は 'a'、'ab'、または 'a' に任意個数の 'b' を続けたものにマッチします。
+
直前の正規表現を 1 回以上繰り返したものにマッチさせる結果の正規表現にします。例えば
ab+
は 'a' に 1 つ以上の 'b' が続いたものにマッチし、単なる 'a' にはマッチしません。
?
直前の正規表現を 0 回か 1 回繰り返したものにマッチさせる結果の正規表現にします。例えば
ab?
は 'a' あるいは 'ab' にマッチします。
*?
,+?
,??
'*'
、'+'
、および'?'
修飾子は全て 貪欲 (greedy) マッチで、できるだけ多くのテキストにマッチします。この挙動が望ましくない時もあります。例えば正規表現<.*>
が'<a> b <c>'
に対してマッチされると、'<a>'
だけでなく文字列全体にマッチしてしまいます。修飾子の後に?
を追加すると、 非貪欲 (non-greedy) あるいは 最小 (minimal) のマッチが行われ、できるだけ 少ない 文字にマッチします。正規表現<.*?>
を使うと'<a>'
だけにマッチします。
{m}
直前の正規表現をちょうど m 回繰り返したものにマッチさせるよう指定します。それより少ないマッチでは正規表現全体がマッチしません。例えば、
a{6}
は 6 個ちょうどの'a'
文字にマッチしますが、 5 個ではマッチしません。{m,n}
直前の正規表現を m 回から n 回、できるだけ多く繰り返したものにマッチさせる結果の正規表現にします。例えば、
a{3,5}
は、3 個から 5 個の'a'
文字にマッチします。m を省略すると下限は 0 に指定され、n を省略すると上限は無限に指定されます。例として、a{4,}b
は'aaaab'
や、1,000 個の'a'
文字に'b'
が続いたものにマッチしますが、'aaab'
にはマッチしません。コンマは省略できません、省略すると修飾子が上で述べた形式と混同されてしまうからです。{m,n}?
結果の正規表現は、前にある正規表現を、m 回から n 回まで繰り返したものにマッチし、できるだけ 少なく 繰り返したものにマッチするようにします。これは、前の修飾子の非貪欲版です。例えば、 6 文字文字列
'aaaaaa'
では、a{3,5}
は、5 個の'a'
文字にマッチしますが、a{3,5}?
は 3 個の文字にマッチするだけです。
\
特殊文字をエスケープ (
'*'
や'?'
などの文字にマッチできるようにする) し、または特殊シーケンスを合図します。特殊シーケンスは後で議論します。パターンを表現するのに raw 文字列を使っていないのであれば、 Python ももまた、バックスラッシュを文字列リテラルでエスケープシーケンスとして使うことを思い出して下さい。そのエスケープシーケンスを Python のパーザが認識しないなら、そのバックスラッシュとそれに続く文字が結果の文字列に含まれます。しかし、Python が結果のシーケンスを認識するなら、そのバックスラッシュは 2 回繰り返さなければいけません。これは複雑で理解しにくいので、ごく単純な表現以外は、全て raw 文字列を使うことを強く推奨します。
[]
文字の集合を指定するのに使います。集合の中では:
文字を個別に指定できます。
[amk]
は'a'
、'm'
または'k'
にマッチします。
連続した文字の範囲を、
'-'
を2 つの文字で挟んで指定できます。例えば、[a-z]
はあらゆる小文字の ASCII 文字にマッチします。[0-5][0-9]
は00
から59
まで全ての 2 桁の数字にマッチします。[0-9A-Fa-f]
は任意の 16 進数字にマッチします。-
がエスケープされているか (例:[a\-z]
)、先頭や末尾の文字にされていると (例:[-a]
や[a-]
)、リテラル'-'
にマッチします。集合の中では、特殊文字はその特殊な意味を失います。例えば
[(+*)]
はリテラル文字'('
、'+'
、'*'
、または')'
のどれにでもマッチします。
補集合 をとって範囲内にない文字にマッチできます。集合の最初の文字が
'^'
なら、集合に 含まれない 全ての文字にマッチします。例えば、[^5]
は'5'
を除くあらゆる文字にマッチし、[^^]
は'^'
を除くあらゆる文字にマッチします。^
は集合の最初の文字でなければ特別の意味を持ちません。集合の中でリテラル
']'
にマッチさせるには、その前にバックスラッシュをつけるか、集合の先頭に置きます。例えば、[()[\]{}]
と[]()[{}]
はどちらも括弧にマッチします。
Unicode Technical Standard #18 にあるような集合の入れ子や集合操作が将来追加される可能性があります。これは構文を変化させるもので、この変化を容易にするために、さしあたって曖昧な事例には
FutureWarning
が送出されます。これはリテラル'['
で始まる集合や、リテラル文字の連続'--'
、'&&'
、'~~'
および'||'
を含む集合を含みます。警告を避けるにはバックスラッシュでエスケープしてください。
バージョン 3.7 で変更: 文字セットが将来意味論的に変化する構造を含むなら
FutureWarning
が送出されます。
|
A と B を任意の正規表現として、
A|B
は A と B のいずれかにマッチする正規表現を作成します。この方法で任意の数の正規表現を'|'
で分離できます。これはグループ (下記参照) 中でも使えます。対象文字列を走査するとき、'|'
で分離された正規表現は左から右へ順に試されます。一つのパターンが完全にマッチしたとき、そのパターン枝が受理されます。つまり、ひとたび A がマッチしてしまえば、例え B によって全体のマッチが長くなるとしても、 B はもはや走査されません。言いかえると、'|'
演算子は決して貪欲にはなりません。リテラル'|'
にマッチするには、\|
を使うか、[|]
のように文字クラス中に囲みます。
(...)
丸括弧で囲まれた正規表現にマッチするとともに、グループの開始と終了を表します。グループの中身は以下で述べるように、マッチが実行された後で回収したり、その文字列中で以降
\number
特殊シーケンスでマッチしたりできます。リテラル'('
や')'
にマッチするには、\(
や\)
を使うか、文字クラス中に囲みます:[(]
、[)]
。
(?...)
これは拡張記法です (
'('
に続く'?'
はそれ以上の意味を持ちません) 。'?'
に続く最初の文字がこの構造の意味と特有の構文を決定します。拡張は一般に新しいグループを作成しません。ただし(?P<name>...)
はこの法則の唯一の例外です。現在サポートされている拡張は以下の通りです。(?aiLmsux)
(
'a'
、'i'
、'L'
、'm'
、's'
、'u'
、'x'
の集合から 1 文字以上。) このグループは空文字列にマッチします。文字は正規表現全体に、対応するフラグを設定します。re.A
(ASCII 限定マッチング)、re.I
(大文字・小文字を区別しない)、re.L
(ロケール依存)、re.M
(複数行)、re.S
(ドットが全てにマッチ)、re.U
(Unicode マッチング)、re.X
(冗長)。 (各フラグについては モジュールコンテンツ で説明します。) これは、 flag 引数をre.compile()
関数に渡すのではなく、フラグを正規表現の一部として含めたいときに便利です。フラグは表現文字列の先頭で使うべきです。
(?:...)
普通の丸括弧の、キャプチャしない版です。丸括弧で囲まれた正規表現にマッチしますが、このグループがマッチした部分文字列は、マッチを実行したあとで回収することも、そのパターン中で以降参照することも できません 。
(?aiLmsux-imsx:...)
(
'a'
、'i'
、'L'
、'm'
、's'
、'u'
、'x'
の集合から 0 文字以上、必要ならさらに'-'
に続けて'i'
、'm'
、's'
、'x'
の集合から 1 文字以上。) 文字は表現の一部に、対応するフラグを設定または除去します。re.A
(ASCII 限定マッチング)、re.I
(大文字・小文字を区別しない)、re.L
(ロケール依存)、re.M
(複数行)、re.S
(ドットが全てにマッチ)、re.U
(Unicode マッチング)、re.X
(冗長)。 (各フラグについては モジュールコンテンツ で説明します。)文字
'a'
、'L'
および'u'
は相互に排他であり、組み合わせることも'-'
に続けることもできません。その代わり、これらの内一つがインライングループ中に現れると、外側のグループでのマッチングモードを上書きします。 Unicode パターン中では(?a:...)
は ASCII 限定マッチングに切り替え、(?u:...)
は Unicode マッチング (デフォルト) に切り替えます。バイト列パターン中では、(?L:...)
はロケール依存マッチングに切り替え、(?a:...)
は ASCII 限定マッチング (デフォルト) に切り替えます。この上書きは狭いインライングループにのみ影響し、元のマッチングモードはグループ外では復元されます。バージョン 3.6 で追加.
バージョン 3.7 で変更: 文字
'a'
、'L'
および'u'
もグループ中で使えます。
(?P<name>...)
通常の丸括弧に似ていますが、このグループがマッチした部分文字列はシンボリックグループ名 name でアクセスできます。グループ名は有効な Python 識別子でなければならず、各グループ名は 1 個の正規表現内で一度だけ定義されていなければなりません。シンボリックグループは、そのグループが名前付けされていなかったかのように番号付けされたグループでもあります。
名前付きグループは 3 つのコンテキストで参照できます。パターンが
(?P<quote>['\"]).*?(?P=quote)
(シングルまたはダブルクオートで囲まれた文字列にマッチ) ならば:グループ "quote" を参照するコンテキスト
参照する方法
その同じパターン中
(?P=quote)
(示したとおり)\1
マッチオブジェクト m の処理時
m.group('quote')
m.end('quote')
(など)
re.sub()
の repl 引数へ渡される文字列中\g<quote>
\g<1>
\1
(?P=name)
名前付きグループへの後方参照です。これは name という名前の既出のグループがマッチした文字列にマッチします。
(?#...)
コメントです。括弧の中身は単純に無視されます。
(?=...)
...
が次に続くものにマッチすればマッチしますが、文字列をまったく消費しません。これは 先読みアサーション (lookahead assertion) と呼ばれます。例えば、Isaac (?=Asimov)
は'Isaac '
に、その後に'Asimov'
が続く場合にのみ、マッチします。
(?!...)
...
が次に続くものにマッチしなければマッチします。これは 否定先読みアサーション (negative lookahead assertion) です。例えば、Isaac (?!Asimov)
は'Isaac '
に、その後に'Asimov'
が続か ない 場合にのみ、マッチします。
(?<=...)
その文字列における現在位置の前に、現在位置で終わる
...
とのマッチがあれば、マッチします。これは 後読みアサーション と呼ばれます。(?<=abc)def
は、後読みは 3 文字をバックアップし、含まれているパターンがマッチするか検査するので'abcdef'
にマッチを見つけます。含まれるパターンは、固定長の文字列にのみマッチしなければなりません。すなわち、abc
やa|b
は許されますが、a*
やa{3,4}
は許されません。肯定後読みアサーションで始まるパターンは、検索される文字列の先頭とは決してマッチしないことに注意して下さい。match()
関数ではなくsearch()
関数を使う方が望ましいでしょう:>>> import re >>> m = re.search('(?<=abc)def', 'abcdef') >>> m.group(0) 'def'
この例ではハイフンに続く単語を探します:
>>> m = re.search(r'(?<=-)\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>'
にはマッチしません。
特殊シーケンスは '\'
と以下のリストの文字から構成されます。通常文字が ASCII 数字でも ASCII 文字でもなければ、結果の正規表現は 2 番目の文字にマッチします。例えば、\$
は文字 '$'
にマッチします。
\number
同じ番号のグループの中身にマッチします。グループは 1 から始まる番号をつけられます。例えば、
(.+) \1
は、'the the'
あるいは'55 55'
にマッチしますが、'thethe'
にはマッチしません(グループの後のスペースに注意して下さい)。この特殊シーケンスは最初の 99 グループのうちの一つとのマッチにのみ使えます。 number の最初の桁が 0 であるか、 number が 3 桁の 8 進数であれば、それはグループのマッチとしてではなく、 8 進値 number を持つ文字として解釈されます。文字クラスの'['
と']'
の間では全ての数値エスケープが文字として扱われます。
\A
文字列の先頭でのみマッチします。
\b
空文字列にマッチしますが、単語の先頭か末尾でのみです。単語は単語文字の並びとして定義されます。形式的には、
\b
は\w
と\W
文字 (またはその逆) との、あるいは\w
と文字列の先頭・末尾との境界として定義されます。例えば、r'\bfoo\b'
は'foo'
、'foo.'
、'(foo)'
、'bar foo baz'
にはマッチしますが、'foobar'
や'foo3'
にはマッチしません。デフォルトの Unicode 英数字は Unicode パターン中で使われるものと同じですが、これは
ASCII
フラグを使って変更できます。LOCALE
フラグが使われているなら単語の境界は現在のロケールによって決定されます。Python の文字列リテラルとの互換性のため、文字列範囲中では、\b
は後退 (backspace) 文字を表します。
\B
空文字列にマッチしますが、それが単語の先頭か末尾 でない ときのみです。つまり
r'py\B'
は'python'
、'py3'
、'py2'
にマッチしますが、'py'
、'py.'
、 または'py!'
にはマッチしません。\B
は\b
のちょうど反対で、 Unicode パターンにおける単語文字は Unicode 英数字およびアンダースコアですが、 これはASCII
フラグを使って変更できます。LOCALE
フラグが使われているなら単語の境界は現在のロケールによって決定されます。
\d
- Unicode (str) パターンでは:
任意の Unicode 10 進数字 (Unicode 文字カテゴリ [Nd]) にマッチします。これは
[0-9]
とその他多数の数字を含みます。ASCII
フラグが使われているなら[0-9]
のみにマッチします。- 8 ビット (bytes) パターンでは:
任意の 10 進数字にマッチします。これは
[0-9]
と等価です。
\D
10 進数字でない任意の文字にマッチします。これは
\d
の反対です。ASCII
フラグが使われているならこれは[^0-9]
と等価になります。
\s
- Unicode (str) パターンでは:
Unicode 空白文字 (これは
[ \t\n\r\f\v]
その他多くの文字、例えば多くの言語におけるタイポグラフィ規則で定義されたノーブレークスペースなどを含みます) にマッチします。ASCII
フラグが使われているなら、[ \t\n\r\f\v]
のみにマッチします。- 8 ビット (bytes) パターンでは:
ASCII 文字セットで空白文字と見なされる文字にマッチします。これは
[ \t\n\r\f\v]
と等価です。
\S
空白文字ではない任意の文字にマッチします。これは
\s
の反対です。ASCII
フラグが使われているならこれは[^ \t\n\r\f\v]
と等価になります。
\w
\W
単語文字ではない任意の文字にマッチします。これは
\w
の反対です。ASCII
フラグが使われているなら、これは[^a-zA-Z0-9_]
と等価になります。LOCALE
フラグが使われているなら、現在のロケールの英数字でもアンダースコアでもない文字にマッチします。
\Z
文字列の末尾でのみマッチします。
Python 文字列リテラルでサポートされている標準エスケープのほとんども正規表現パーザで受理されます:
\a \b \f \n
\r \t \u \U
\v \x \\
(\b
は単語の境界を表すのに使われ、文字クラス中でのみ "後退 (backspace)" 文字を意味することに注意してください。)
'\u'
および '\U'
エスケープシーケンスは Unicode パターン内でのみ認識されます。バイト列ではエラーとなります。
ASCII 文字のエスケープで未知のものは将来使うために予約されていて、エラーとして扱われます。
8 進エスケープは限られた形式でのみ含まれます。その最初の桁が 0 であるか、それが 3 桁の 8 進数であるならば、それは 8 進エスケープと見なされます。そうでなければ、それはグループ参照です。文字列リテラルでは、8 進エスケープは常にたかだか 3 桁長です。
バージョン 3.3 で変更: '\u'
と '\U'
エスケープシーケンスが追加されました。
バージョン 3.6 で変更: '\'
と ASCII 文字からなる未知のエスケープはエラーになります。
モジュールコンテンツ¶
このモジュールはいくつかの関数、定数、例外を定義します。このうちいくつかの関数は、コンパイル済み正規表現がそなえる完全な機能のメソッドを簡易にしたものです。些細なものを除くほとんどのアプリケーションは常にコンパイル済み形式を使います。
バージョン 3.6 で変更: フラグ定数は、enum.IntFlag
のサブクラスである RegexFlag
のインスタンスになりました。
-
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
に、完全な Unicode マッチングではなく ASCII 限定マッチングを行わせます。これは Unicode パターンでのみ意味があり、バイト列パターンでは無視されます。インラインフラグの(?a)
に相当します。後方互換性のため、
re.U
フラグ (と同義のre.UNICODE
および埋め込みで使用する(?u)
) はまだ存在しますが、Python 3 では文字列のマッチがデフォルトで Unicode (そしてバイト列では Unicode マッチングが扱えない) なので冗長です。
-
re.
DEBUG
¶ コンパイル済み表現に関するデバッグ情報を表示します。相当するインラインフラグはありません。
-
re.
I
¶ -
re.
IGNORECASE
¶ 大文字・小文字を区別しないマッチングを行います;
[A-Z]
のような正規表現は小文字にもマッチします。re.ASCII
フラグを使い、非 ASCII マッチが無効化されていない限り、 (Ü
がü
にマッチするような) 完全な Unicode マッチングも有効です。re.LOCALE
フラグも一緒に使われていない限り、現在のロケールがこのフラグの効果を変更することはありません。 インラインフラグの(?i)
に相当します。Unicode パターン
[a-z]
または[A-Z]
がIGNORECASE
フラグとあわせて使われたとき、52 の ASCII 文字に加えて 4 の非 ASCII 文字 'İ' (U+0130, Latin capital letter I with dot above) 、 'ı' (U+0131, Latin small letter dotless i) 、 'ſ' (U+017F, Latin small letter long s) および 'K' (U+212A, Kelvin sign) にマッチすることに注意してください。ASCII
フラグが使われているなら、文字 'a' から 'z' および 'A' から 'Z' にのみマッチします。
-
re.
L
¶ -
re.
LOCALE
¶ \w
、\W
、\b
、\B
および大文字・小文字を区別しないマッチングを、現在のロケールに依存させます。ロケールの仕組みは信頼できず、一度に一つの "文化" しか扱えず、 8 ビットロケールでしか働かないので、このフラグを使うことは推奨されません。Python 3 において Unicode (str) パターンでは Unicode マッチングはデフォルトですでに有効にされていて、異なるロケールや言語を扱えます。インラインフラグの(?L)
に相当します。バージョン 3.7 で変更:
re.LOCALE
フラグがあるコンパイル済み正規表現オブジェクトはコンパイル時のロケールに依存しなくなりました。マッチング時のロケールのみがマッチングの結果に影響します。
-
re.
M
¶ -
re.
MULTILINE
¶ 指定されていると、パターン文字
'^'
は文字列の先頭で、および各行の先頭 (各改行の直後) で、マッチします。そしてパターン文字'$'
は文字列の末尾で、および各行の末尾 (各改行の直前) で、マッチします。デフォルトでは、'^'
は文字列の先頭でのみ、'$'
は文字列の末尾および文字列の末尾の改行 (もしあれば) の直前でのみマッチします。インラインフラグの(?m)
に相当します。
-
re.
S
¶ -
re.
DOTALL
¶ '.'
特殊文字を、改行を含むあらゆる文字にマッチさせます。このフラグがなければ、'.'
は、改行 以外の あらゆる文字とマッチします。インラインフラグの(?s)
に相当します。
-
re.
X
¶ -
re.
VERBOSE
¶ このフラグは正規表現を、パターンの論理的な節を視覚的に分割し、コメントを加えることで、見た目よく読みやすく書けるようにします。パターン中の空白は、文字クラス中にあるときと、エスケープされていないバックスラッシュの後にあるときと、
*?
、(?:
や(?P<...>
のようなトークン中を除いて無視されます。ある行が文字クラス中でもエスケープされていないバックスラッシュの後でもない#
を含むなら、一番左のそのような#
から行末までの全ての文字は無視されます。つまり、10 進数字にマッチする下記のふたつの正規表現オブジェクトは、機能的に等価です:
a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X) b = re.compile(r"\d+\.\d*")
インラインフラグの
(?x)
に相当します。
-
re.
search
(pattern, string, flags=0)¶ string を走査し、正規表現 pattern がマッチを生じさせる最初の場所を探して、対応する マッチオブジェクト を返します。文字列内にパターンにマッチする場所がなければ
None
を返します。これは文字列のどこかで長さ 0 のマッチを見つけるのとは異なることに注意してください。
-
re.
match
(pattern, string, flags=0)¶ string の先頭で 0 個以上の文字が正規表現 pattern にマッチすれば、対応する マッチオブジェクト を返します。文字列がパターンにマッチしなければ
None
を返します。これは長さ 0 のマッチとは異なることに注意して下さい。MULTILINE
モードにおいても、re.match()
は各行の先頭でマッチするのではなく、文字列の先頭でのみマッチすることに注意してください。string 中のどこででもマッチさせたいなら、代わりに
search()
を使ってください (search() vs. match() も参照してください)。
-
re.
fullmatch
(pattern, string, flags=0)¶ string 全体が正規表現 pattern にマッチするなら、対応する マッチオブジェクト を返します。文字列がパターンにマッチしないなら
None
を返します。これは長さ 0 のマッチとは異なることに注意して下さい。バージョン 3.4 で追加.
-
re.
split
(pattern, string, maxsplit=0, flags=0)¶ string を、出現した pattern で分割します。 pattern 中でキャプチャの丸括弧が使われていれば、パターン中の全てのグループのテキストも結果のリストの一部として返されます。maxsplit が 0 でなければ、分割は最大 maxsplit 回起こり、残りの文字列はリストの最終要素として返されます。
>>> re.split(r'\W+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split(r'(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split(r'\W+', 'Words, words, words.', 1) ['Words', 'words, words.'] >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE) ['0', '3', '9']
セパレータ中にキャプチャグループがあり、それが文字列の先頭にマッチするなら、結果は空文字列で始まります。同じことが文字列の末尾にも言えます。
>>> re.split(r'(\W+)', '...words, words...') ['', '...', 'words', ', ', 'words', '...', '']
そうして、結果のリストにおいて、セパレータの構成要素は常に同じ相対的インデックスに見つかります。
パターンへの空マッチは、直前の空マッチに隣接していないときのみ文字列を分割します。
>>> re.split(r'\b', 'Words, words, words.') ['', 'Words', ', ', 'words', ', ', 'words', '.'] >>> re.split(r'\W*', '...words...') ['', '', 'w', 'o', 'r', 'd', 's', '', ''] >>> re.split(r'(\W*)', '...words...') ['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']
バージョン 3.1 で変更: オプションの flags 引数が追加されました。
バージョン 3.7 で変更: 空文字列にマッチしうるパターンでの分割をサポートするようになりました。
-
re.
findall
(pattern, string, flags=0)¶ string 中の pattern による全ての重複しないマッチを、文字列のリストとして返します。 string は左から右へ走査され、マッチは見つかった順で返されます。パターン中に 1 つ以上のグループがあれば、グループのリストを返します。パターンに複数のグループがあればタプルのリストになります。空マッチは結果に含まれます。
バージョン 3.7 で変更: 空でないマッチが前の空マッチの直後から始められるようになりました。
-
re.
finditer
(pattern, string, flags=0)¶ string 中の正規表現 pattern の重複しないマッチ全てに渡る マッチオブジェクト を yield する イテレータ を返します。 string は左から右へ走査され、マッチは見つかった順で返されます。空マッチは結果に含まれます。
バージョン 3.7 で変更: 空でないマッチが前の空マッチの直後から始められるようになりました。
-
re.
sub
(pattern, repl, string, count=0, flags=0)¶ string 中に出現する最も左の重複しない pattern を置換 repl で置換することで得られる文字列を返します。 パターンが見つからない場合、 string がそのまま返されます。 repl は文字列または関数です。 repl が文字列の場合は、その中の全てのバックスラッシュエスケープが処理されます。
\n
は 1 つの改行文字に変換され、\r
はキャリッジリターンに変換される、などです。 ASCII 文字のエスケープで未知のものは将来使うために予約されていて、エラーとして扱われます。 それ以外の\&
のような未知のエスケープは残されます。\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'
パターンは、文字列でも パターンオブジェクト でも構いません。
オプション引数 count は出現したパターンを置換する最大の回数です。 count は非負整数です。省略されるか 0 なら、出現した全てが置換されます。パターンへの空マッチは前の空マッチに隣接していないときのみ置換されるので、
sub('x*', '-', 'abxd')
は'-a-b--d-'
を返します。文字列型 repl 引数では、上で述べた文字エスケープや後方参照に加えて、
\g<name>
は(?P<name>...)
構文で定義されたname
という名前のグループがマッチした部分文字列を使い、\g<number>
は対応するグループ番号を使います。よって\g<2>
は\2
と等価ですが、\g<2>0
のような置換においても曖昧になりません。\20
は、グループ 20 への参照として解釈され、グループ 2 への参照にリテラル文字'0'
が続いたものとしては解釈されません。後方参照\g<0>
は正規表現とマッチした部分文字列全体で置き換わります。バージョン 3.1 で変更: オプションの flags 引数が追加されました。
バージョン 3.5 で変更: マッチしなかったグループは空文字列に置き換えられます。
バージョン 3.6 で変更: pattern 中に
'\'
と ASCII 文字からなる未知のエスケープがあると、エラーになります。バージョン 3.7 で変更: repl 中に
'\'
と ASCII 文字からなる未知のエスケープがあると、エラーになります。バージョン 3.7 で変更: パターンへの空マッチは前の空でないマッチに隣接しているとき置き換えられます。
-
re.
subn
(pattern, repl, string, count=0, flags=0)¶ sub()
と同じ操作を行いますが、タプル(new_string、 number_of_subs_made)
を返します。バージョン 3.1 で変更: オプションの flags 引数が追加されました。
バージョン 3.5 で変更: マッチしなかったグループは空文字列に置き換えられます。
-
re.
escape
(pattern)¶ pattern 中の特殊文字をエスケープします。これは正規表現メタ文字を含みうる任意のリテラル文字列にマッチしたい時に便利です。
>>> print(re.escape('http://www.python.org')) http://www\.python\.org >>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:" >>> print('[%s]+' % re.escape(legal_chars)) [abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:]+ >>> operators = ['+', '-', '*', '/', '**'] >>> print('|'.join(map(re.escape, sorted(operators, reverse=True)))) /|\-|\+|\*\*|\*
この関数は、バックスラッシュのみをエスケープするべき
sub()
およびsubn()
における置換文字列に使われてはなりません。例えば:>>> digits_re = r'\d+' >>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings' >>> print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample)) /usr/sbin/sendmail - \d+ errors, \d+ warnings
バージョン 3.3 で変更:
'_'
文字がエスケープされなくなりました。バージョン 3.7 で変更: 正規表現で特別な意味を持つ文字だけがエスケープされます。結果として、
'!'
、'"'
、'%'
、"'"
、','
、'/'
、':'
、';'
、'<'
、'='
、'>'
、'@'
、 と"`"
はもはやエスケープされません。
-
re.
purge
()¶ 正規表現キャッシュをクリアします。
-
exception
re.
error
(msg, pattern=None, pos=None)¶ ここの関数のいずれかに渡された文字列が有効な正規表現ではない (例: 括弧が対になっていない) とき、またはコンパイルやマッチングの際にその他なんらかのエラーが発生した場合に送出される例外です。文字列にパターンとマッチする部分がなくても、それはエラーではありません。エラーインスタンスには、次のような追加の属性があります。
-
msg
¶ フォーマットされていないエラーメッセージです。
-
pattern
¶ 正規表現のパターンです。
-
pos
¶ pattern のコンパイルに失敗した場所のインデックスです (
None
の場合もあります)。
-
lineno
¶ pos に対応する行です (
None
の場合もあります)。
-
colno
¶ pos に対応する列です (
None
の場合もあります)。
バージョン 3.5 で変更: 追加の属性が追加されました。
-
正規表現オブジェクト¶
コンパイル済み正規表現オブジェクトは以下のメソッドと属性をサポートします:
-
Pattern.
search
(string[, pos[, endpos]])¶ string を走査し、この正規表現がマッチを生じさせる最初の場所を探して、対応する マッチオブジェクト を返します。文字列内にパターンにマッチする場所がなければ
None
を返します。これは文字列内のある場所で長さが 0 のマッチが見つかった場合とは異なることに注意してください。オプションの第二引数 pos は、文字列のどこから探し始めるかを指定するインデックスで、デフォルトでは 0 です。これは文字列のスライスと完全には同じではありません。パターン文字
'^'
は本当の文字列の先頭と改行の直後でマッチしますが、検索を開始するインデックスでマッチするとは限りません。オプションの引数 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 <re.Match object; span=(0, 1), match='d'> >>> pattern.search("dog", 1) # No match; search doesn't include the "d"
-
Pattern.
match
(string[, pos[, endpos]])¶ string の 先頭 で 0 文字以上がこの正規表現とマッチするなら、対応する マッチオブジェクト を返します。文字列がパターンにマッチしなければ
None
を返します。これは長さ 0 のマッチとは異なることに注意してください。オプションの 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". <re.Match object; span=(1, 2), match='o'>
string 中のどこででもマッチさせたいなら、代わりに
search()
を使ってください (search() vs. match() も参照してください)。
-
Pattern.
fullmatch
(string[, pos[, endpos]])¶ string 全体がこの正規表現にマッチすれば、対応する マッチオブジェクト を返します。文字列がパターンにマッチしなければ
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. <re.Match object; span=(1, 3), match='og'>
バージョン 3.4 で追加.
-
Pattern.
findall
(string[, pos[, endpos]])¶ findall()
関数にこのコンパイル済みパターンを使うのと似ていますが、オプションの pos および endpos 引数でsearch()
のように検索範囲を制限できます。
-
Pattern.
finditer
(string[, pos[, endpos]])¶ finditer()
関数にこのコンパイル済みパターンを使うのと似ていますが、オプションの pos および endpos 引数でsearch()
のように検索範囲を制限できます。
-
Pattern.
flags
¶ 正規表現のマッチングフラグです。これは
compile()
に与えられたフラグ、パターン中の(?...)
インラインフラグ、およびパターンが Unicode 文字列だった時のUNICODE
のような暗黙のフラグの組み合わせです。
-
Pattern.
groups
¶ パターン中のキャプチャグループの数です。
-
Pattern.
groupindex
¶ (?P<id>)
で定義されたあらゆるシンボリックグループ名をグループ番号へ写像する辞書です。シンボリックグループがパターン中で全く使われていなければ、この辞書は空です。
-
Pattern.
pattern
¶ パターンオブジェクトがコンパイルされた元のパターン文字列です。
バージョン 3.7 で変更: copy.copy()
および copy.deepcopy()
をサポートするようになりました。コンパイル済み正規表現オブジェクトはアトミックであると見なされます。
マッチオブジェクト¶
マッチオブジェクトのブール値は常に 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 つ以上のサブグループを返します。引数が 1 つなら結果は 1 つの文字列です。複数の引数があれば、結果は引数ごとに 1 項目のタプルです。引数がなければ、 group1 はデフォルトで 0 (マッチ全体が返される) です。 groupN 引数が 0 なら、対応する返り値はマッチした文字列全体です。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.
__getitem__
(g)¶ これは
m.group(g)
と同等です。これでマッチの個別のグループに簡単にアクセスできます:>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") >>> m[0] # The entire match 'Isaac Newton' >>> m[1] # The first parenthesized subgroup. 'Isaac' >>> m[2] # The second parenthesized subgroup. 'Newton'
バージョン 3.6 で追加.
-
Match.
groups
(default=None)¶ このマッチの、1 からパターン中のグループ数まで、全てのサブグループを含むタプルを返します。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 はデフォルトで 0 (マッチした部分文字列全体という意味) です。 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 はデフォルトで 0 、マッチ全体です。
-
Match.
pos
¶ 正規表現オブジェクト の
search()
やmatch()
に渡された pos の値です。これは正規表現エンジンがマッチを探し始める位置の文字列のインデックスです。
-
Match.
endpos
¶ 正規表現オブジェクト の
search()
やmatch()
に渡された endpos の値です。これは正規表現エンジンがそれ以上は進まない文字列のインデックスです。
-
Match.
lastindex
¶ 最後にマッチしたキャプチャグループの整数インデックスです。どのグループも全くマッチしなければ
None
です。例えば、表現(a)b
、((a)(b))
や((ab))
が'ab'
に適用されるとlastindex == 1
となり、同じ文字列に(a)(b)
が適用されるとlastindex == 2
となります。
-
Match.
lastgroup
¶ 最後にマッチしたキャプチャグループの名前です。そのグループに名前がないか、どのグループも全くマッチしていなければ
None
です。
-
Match.
re
¶ このマッチインスタンスを生じさせた
match()
またはsearch()
メソッドの属する 正規表現オブジェクト です。
バージョン 3.7 で変更: copy.copy()
および copy.deepcopy()
をサポートするようになりました。マッチオブジェクトはアトミックであると見なされます。
正規表現の例¶
ペアの確認¶
この例では、マッチオブジェクトを少し美しく表示するために、この補助関数を使用します:
def displaymatch(match):
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
あなたがポーカープログラムを書いているとします。プレイヤーの手札は 5 文字の文字列によって表され、それぞれの文字が 1 枚のカードを表します。 "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'
scanf() をシミュレートする¶
Python には現在のところ、 scanf()
に相当するものがありません。正規表現は一般的に、 scanf()
のフォーマット文字列より強力ですが、冗長でもあります。以下の表に、 scanf()
のフォーマットトークンと正規表現のおおよその対応付けを示します。
|
正規表現 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
以下のような文字列からファイル名と数を抽出するには
/usr/sbin/sendmail - 0 errors, 4 warnings
以下のように scanf()
フォーマットを使えます
%s - %d errors, %d warnings
等価な正規表現はこうです
(\S+) - (\d+) errors, (\d+) warnings
search() vs. match()¶
Python は正規表現ベースの 2 つの異なる基本的な関数、文字列の先頭でのみのマッチを確認する re.match()
および、文字列中の位置にかかわらずマッチを確認する re.search()
(これが Perl でのデフォルトの挙動です) を提供しています。
例えば:
>>> re.match("c", "abcdef") # No match
>>> re.search("c", "abcdef") # Match
<re.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
<re.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
<re.Match object; span=(4, 5), match='X'>
電話帳を作る¶
split()
は渡されたパターンで文字列を分割してリストにします。このメソッドは、テキストデータをデータ構造に変換して、読みやすくしたり、以下の例で実演する電話帳作成のように Python で編集したりしやすくするのに、非常に役に立ちます。
まず、ここに入力があります。普通はファイルから持ってくることもありますが、ここでは三重クオート文字列構文を使います:
>>> 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']]
テキストの秘匿¶
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.'
全ての副詞を見つける¶
search()
は最初のパターンにのみマッチしますが、 findall()
は出現する 全ての パターンにマッチします。例えば、ライターがあるテキストの全ての副詞を見つけたいなら、以下のように findall()
を使えます:
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']
全ての副詞とその位置を見つける¶
パターンの全てのマッチについて、マッチしたテキスト以上の情報が必要なら、文字列ではなく マッチオブジェクト を返す 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
Raw 文字列記法¶
Raw 文字列記法 (r"text"
) で正規表現をまともに保てます。それがなければ、正規表現中のバックスラッシュ ('\'
) を個々にバックスラッシュを前置してエスケープしなければなりません。例えば、以下の 2 行のコードは機能的に等価です:
>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
リテラルのバックスラッシュにマッチさせたいなら、正規表現中ではエスケープする必要があります。Raw 文字列記法では、r"\\"
になります。Raw 文字列記法を用いないと、"\\\\"
としなくてはならず、以下のコードは機能的に等価です:
>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
トークナイザを書く¶
トークナイザやスキャナ は文字列を解析し、文字のグループにカテゴリ分けします。これはコンパイラやインタプリタを書くうえで役立つ第一段階です。
テキストのカテゴリは正規表現で指定されます。この技法では、それらを一つのマスター正規表現に結合し、マッチの連続についてループします:
import collections
import re
Token = collections.namedtuple('Token', ['type', '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()
column = mo.start() - line_start
if kind == 'NUMBER':
value = float(value) if '.' in value else int(value)
elif kind == 'ID' and value in keywords:
kind = value
elif kind == 'NEWLINE':
line_start = mo.end()
line_num += 1
continue
elif kind == 'SKIP':
continue
elif kind == 'MISMATCH':
raise RuntimeError(f'{value!r} unexpected on line {line_num}')
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(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)
- Frie09
Friedl, Jeffrey. Mastering Regular Expressions. 3rd ed., O'Reilly Media, 2009. 当書の第三版ではもはや Python についてまったく取り扱っていませんが、初版では良い正規表現を書くことを綿密に取り扱っていました。