現在地: ホーム ‣ Dive Into Python 3 ‣
難易度: ♦♦♦♢♢
❝ある人々は問題に直面すると、「そうか、正規表現を使うんだ」と考える。こうして彼らは2つの問題を抱えることになる。❞
— Jamie Zawinski
大きなテキストのかたまりから小さなテキストのかけらを取り出すのは挑戦的な課題だ。Pythonの文字列は、検索や置換のためのメソッドとしてindex()
, find()
, split()
, count()
, replace()
などを持っている。しかし、これらのメソッドが機能するのは最も単純な場合に限られる。例えば、index()
メソッドはハードコードされた1つの部分文字列を探すものだが、その検索は常に大文字と小文字が区別される。大文字と小文字を区別しないで検索したいときは、s.lower()
またはs.upper()
を呼び出して、さらに検索文字列の大文字と小文字もそれに合わせなければならない。replace()
メソッドやsplit()
メソッドも同様の制限を持っている。
文字列のメソッドで目的が達成できるのであればそれを使うべきだ。文字列のメソッドは高速でシンプルで読みやすく、それには数多くの利点がある。しかし、if
文や様々な文字列関数を組み合わせて特殊な場合に対処している場合や、文字列を切り刻むためにsplit()
やjoin()
を連鎖して呼びだしている場合は、正規表現へ移行する必要があるかもしれない。
正規表現はパワフルであり、複雑な文字パターンを使って文字列を検索、置換、パースするための(ほぼ)統一された方法だ。正規表現の構文はギチギチしていて普通のコードとは似つかないものだが、結果としては長々と文字列関数を連ねる自前の解法よりももっと読みやすいものになりうる。正規表現の中にコメントを埋め込む方法さえあるので、きめ細かいドキュメントを中に含めておくこともできる。
☞正規表現を他の言語(例えば Perl, JavaScript, PHP)で使ったことがあるのなら、Pythonでの構文もそれに非常に良く似たものだ。reモジュールの概要を読んで、利用可能な関数とそれらの引数についての概観を得てほしい。
⁂
この一連の例は、何年か前に私が仕事で出くわした実際の問題から着想を得ている。その時、私は古いシステムからエクスポートされた番地をきれいに標準化して新しいシステムへインポートしなければならなかった(ほら、だからこれは説明のために作り上げられた例なんかではなく、現実に役立つものなんだ)。この例は私がその問題にどのようにアプローチしたのかを示している。
>>> s = '100 NORTH MAIN ROAD' >>> s.replace('ROAD', 'RD.') ① '100 NORTH MAIN RD.' >>> s = '100 NORTH BROAD ROAD' >>> s.replace('ROAD', 'RD.') ② '100 NORTH BRD. RD.' >>> s[:-4] + s[-4:].replace('ROAD', 'RD.') ③ '100 NORTH BROAD RD.' >>> import re ④ >>> re.sub('ROAD$', 'RD.', s) ⑤ '100 NORTH BROAD RD.'
'ROAD'
が常に省略形の'RD.'
になるようにすることだ。一見したところ、私は文字列メソッドのreplace()
を使えば十分だと思った。何にせよ、すべてのデータが既に大文字になっているので、大文字と小文字の違いは問題にならない。検索文字列は定数'ROAD'
でよい。そして、この簡単そうな例では、s.replace()
は実際に動作する。
'ROAD'
が番地の中で2度現れることに起因している。1つは通りの名前'BROAD'
の一部として現れ、もう1つは'ROAD'
単独で現れる。replace()
メソッドはこれら2つを見つけ出し、両方を機械的に置換してしまう。つまり、破壊された番地が得られてしまうのだ。
'ROAD'
という部分文字列を複数含む番地の問題を解決するために、次のような解法に頼ることができる。番地の最後の4文字 (s[-4:]
) にある'ROAD'
についてのみ検索と置換を行い、その他の部分 (s[:-4]
) はそのままにしておくのだ。しかし、やり方がすでに不格好になってきていることが分かるだろう。例えば、この方式は置換しようとしている文字列の長さに依存している(仮に'STREET'
を'ST.'
で置換したいとすると、s[:-6]
とs[-6:].replace(...)
を使わなければならない)。6ヶ月後に戻ってきたときにこんなものをデバッグしたいだろうか? 私はいやだね。
re
モジュールに入っている。
'ROAD$'
だ。この単純な正規表現は、文字列の末尾に出現する'ROAD'
だけにマッチする。$
は「文字列の末尾」を意味するのだ(これと対称な文字として、キャレット文字^
があり、これは「文字列の先頭」を意味する)。re.sub()
を使ってsから正規表現'ROAD$'
を探して、それを'RD.'
で置換する。これは文字列sの末尾にあるROAD
にはマッチするが、BROAD
の一部として含まれるROAD
は文字列の中間にあるのでマッチしない。
番地をきれいにする話を続けると、私はすぐに、前の例のように文字列の末尾にある'ROAD'
にマッチするだけでは不十分であることを発見した。なぜなら、すべての番地に街路表示があるわけではないからだ。いくつかの番地は単に街路名で終わっている。これは多くの場合に無視できたのだが、街路名が'BROAD'
の場合、この正規表現だと文字列の末尾にある'BROAD'
のうちの'ROAD'
にマッチしてしまう。これは私の望む結果ではない。
>>> s = '100 BROAD' >>> re.sub('ROAD$', 'RD.', s) '100 BRD.' >>> re.sub('\\bROAD$', 'RD.', s) ① '100 BROAD' >>> re.sub(r'\bROAD$', 'RD.', s) ② '100 BROAD' >>> s = '100 BROAD ROAD APT. 3' >>> re.sub(r'\bROAD$', 'RD.', s) ③ '100 BROAD ROAD APT. 3' >>> re.sub(r'\bROAD\b', 'RD.', s) ④ '100 BROAD RD. APT 3'
'ROAD'
にマッチすることだ。これを正規表現で表現するには\b
を使えばよい。この記号は「ここに単語境界がある」ことを意味する。Pythonでは文字列中の'\'
という文字がエスケープされるので話が複雑になる。これはバックスラッシュの災いと呼ばれることもあり、PythonよりもPerlのほうが正規表現が使いやすい1つの理由でもある。反対の面では、Perlは正規表現を他の構文と混ぜ合わせているので、バグ存在するときに、構文にバグがあるのか正規表現にバグがあるのかを区別しにくいかもしれない。
r
という文字を付ければよい。これは「その文字列の中でエスケープしてはいけない」ということをPythonに伝えるもので、例えば、通常'\t'
はタブ文字であるが、r'\t'
は本当のバックスラッシュ\
とそれに続くt
になる。正規表現を使うときは常にRaw文字列を使うことをおすすめする。さもなければ、一瞬でわけが分からなくなってしまう(それでなくとも、正規表現自体がすでにややこしいのだ)。
'ROAD'
は番地に1つの単語として含まれているにも関わらず、街路表記の後ろにアパートの番号があるために、文字列の末尾に現れない。そして'ROAD'
が文字列の末尾にないので、正規表現はマッチせず、re.sub()
を呼び出してもまったく何も置換しないで元の文字列をそのまま返してしまうのだ。
$
文字を取り除いて、もう一つの\b
を加えた。この正規表現はこう読める「文字列中のどこであっても、'ROAD'
を1つの単語として含むものにマッチする」。先頭でも、末尾でも、途中のどこでも、だ。
⁂
ローマ数字というものを、たとえ読むことはできなくても、見たことくらいはあるだろう。古い映画やテレビ番組の著作権表示で見たことがあるかもしれないし("Copyright 1946
" の代わりに "Copyright MCMXLVI
" になっている)、図書館や大学に貢献した人の名前を連ねた壁に書かれているのを見たことがあるかもしれないし("established 1888
" の代わりに "established MDCCCLXXXVIII
" になっている)、書誌参照の中で見たことがあるかもしれない。このローマ数字は、数を表現するための体系の1つで、実際に古代ローマ帝国時代に使われていたものだ(それゆえにローマ数字と呼ばれる)。
ローマ数字には、数を表現するために繰り返されたり組み合わせられたりする文字が7つある。
I = 1
V = 5
X = 10
L = 50
C = 100
D = 500
M = 1000
以下はローマ数字を構築するための一般的な規則である:
I
は1
、II
は2
、そしてIII
は3
だ。VI
は6
で(文字通り「5
と1
」だ)、VII
は7
、VIII
は8
。
I
、X
、C
、M
)は三回まで繰り返せる。4
については、次の5の文字から引いて表さなければならない。つまり、4
をIIII
と表すことはできず、代わりにIV
としなければならないのだ(「5
引く1
」)。40
はXL
と書かれ(「50
引く10
」)、41
はXLI
、42
はXLII
、43
はXLIII
、そして44
はXLIV
と表せられる(「50
引く10
と5
引く1
」)。
9
を作るには、次の10の数から引き算をしなければならない。つまり、8
はVIII
だが、9
はIX
となり(「10
引く1
」)、VIIII
とは書けないのだ(なぜならI
の文字を4回繰り返すことはできないから)。90
はXC
、900
はCM
となる。
10
は必ずX
と表し、VV
とすることはできない。100
もC
であって、LL
とはならない。
DC
は600
だが、CD
はそれとは全く異なる数を表すのだ(400
、「500
引く100
」)。また、CI
は101
だがIC
は適切なローマ数字ですらない(1
を直接100
から引くことはできないからだ。代わりにXCIX
と書かなくてはならない、「100
引く10
、加えて10
引く1
」)。
任意の文字列が正しいローマ数字であることを検証するにはどうしたらよいのだろうか? 1桁ごとに考えていこう。ローマ数字は常に大きい位から小さい位へと書かれるので、まずは最も大きな位である1000の位から始めていこう。1000以上の数では、1000の位はM
という文字の並びで表される。
>>> import re >>> pattern = '^M?M?M?$' ① >>> re.search(pattern, 'M') ② <_sre.SRE_Match object at 0106FB58> >>> re.search(pattern, 'MM') ③ <_sre.SRE_Match object at 0106C290> >>> re.search(pattern, 'MMM') ④ <_sre.SRE_Match object at 0106AA38> >>> re.search(pattern, 'MMMM') ⑤ >>> re.search(pattern, '') ⑥ <_sre.SRE_Match object at 0106F4A8>
^
は文字列の先頭から始まるものにマッチする。もしこれがないと、パターンはM
という文字がどこにあってもマッチしてしまう。これは望む結果ではない。M
という文字が存在するのであれば、それが文字列の先頭にあることを確認したい。M?
は1つのM
という文字に任意でマッチする(つまり、M
が0回または1回現れるものにマッチする)。それが3回繰り返されているので、0回から3回M
が連続しているところにマッチすることになる。そして$
は文字列の末尾にマッチする。これが先頭にある^
と同時に使われると、パターンが文字列全体にマッチしなければならなくなり、M
の前後に他の文字が入ったものにはマッチしなくなる。
re
モジュールの核心となるのがsearch()
関数だ。この関数は正規表現(pattern)と、その正規表現とマッチさせるための文字列('M'
)を引数に取る。マッチがもし見つかれば、search()
はマッチを表現するための様々なメソッドを持ったオブジェクトを返す。マッチが1つも見つからなければ、search()
はPythonのNull値であるNone
を返す。現段階で関心があるのはパターンがマッチするかどうかだけであり、それはsearch()
の戻り値を見るだけで判断できる。'M'
はこの正規表現にマッチする。なぜなら1つ目の省略可能なM
はマッチし、2つ目と3つ目の省略可能なM
は無視されるからだ。
'MM'
はマッチする。1つ目と2つ目の省略可能なM
がマッチし、3つ目のM
は無視されるからだ。
'MMM'
はマッチする。3つすべてのM
がマッチするからだ。
'MMMM'
はマッチしない。3つすべてのM
がマッチするが、この正規表現は更に文字列がそこで終わっていることを要求している($
という文字があるからだ)。しかし文字列はまだ終わっていない(4番目のM
がある)。したがってsearch()
はNone
を返す。
M
は省略可能だからだ。
100の位は1000の位よりも難しい。なぜなら、それぞれの値に応じていくつかの異なる表現方法が使われるからだ。
100 = C
200 = CC
300 = CCC
400 = CD
500 = D
600 = DC
700 = DCC
800 = DCCC
900 = CM
つまり、あり得るパターンは4つある:
CM
CD
C
(100の位が0の場合に0個になる)
D
の後に、0個から3個のC
が続いたもの。
最後の2つのパターンは1つにまとめることができる。
D
の後に、0個から3個のC
が続いたもの。
次の例はローマ数字の100の位をどうやって検証するのかを示している。
>>> import re >>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)$' ① >>> re.search(pattern, 'MCM') ② <_sre.SRE_Match object at 01070390> >>> re.search(pattern, 'MD') ③ <_sre.SRE_Match object at 01073A50> >>> re.search(pattern, 'MMMCCC') ④ <_sre.SRE_Match object at 010748A8> >>> re.search(pattern, 'MCMC') ⑤ >>> re.search(pattern, '') ⑥ <_sre.SRE_Match object at 01071D98>
^
)、次に1000の位をチェックする (M?M?M?
)。ここからの括弧の中が新しい部分であり、同時に使われることのない3種類のパターン、すなわちCM
、CD
、そしてD?C?C?C?
(これは省略可能なD
の後に、0個から3個のC
が続いたもの)の3つが縦棒によって区切られて定義されている。正規表現のパーサはこれら3つそれぞれのパターンを(右から左へ)順番にチェックし、最初にマッチしたものを取り上げて、残りは無視する。
'MCM'
はマッチする。なぜなら最初のM
はマッチし、2番目と3番目のM
は無視され、そしてCM
がマッチするからだ(従ってCD
とD?C?C?C?
のパターンは試されてすらいない)。MCM
は1900
をローマ数字で表したものだ。
'MD'
はマッチする。なぜなら最初のM
はマッチし、2つ目と3つ目のM
は無視され、D?C?C?C?
のパターンはD
にマッチするからだ(3つのC
はそれぞれが省略可能なので無視される)。MD
は1500
をローマ数字で表したものだ。
'MMMCCC'
はマッチする。なぜなら3つのM
全部がマッチし、D?C?C?C?
パターンがCCC
にマッチするからだ(D
は省略可能なので無視される)。MMMCCC
は3300
をローマ数字で表したものだ。
'MCMC'
はマッチしない。最初のM
はマッチし、2つ目と3つ目のM
は無視され、CM
はマッチする。しかし、まだ文字列の末尾に到達していない(まだマッチしていないC
がある)ので、$
はマッチしない。C
はパターンD?C?C?C?
の一部としてはマッチしない。なぜならCM
のパターンが既にマッチしているので、D?C?C?C?
のパターンを用いる余地は無いからだ。
M
は省略可能なので無視され、しかも空文字列はパターンD?C?C?C?
(これらの文字はどれも省略可能なので無視される)にマッチするからだ。
やーれやれ! 正規表現がたちどころに汚くなってしまうことが分かっただろうか? そして、これでもまだローマ数字の1000の位と100の位をカバーしただけなのだ。だが、ここまでくれば、後の10の位と1の位は簡単にできるだろう。というのも、これらはまったく同じようなパターンだからだ。しかし、次はパターンを表現する別の方法を見てみよう。
⁂
{n,m}
構文を使う前節では、同じ文字が最高で3回まで繰り返されるパターンを扱った。正規表現には、これを表現する別の方法が存在し、人によってはこちらの方が読みやすいだろう。まずは前の例で使ったやり方を見てみよう。
>>> import re >>> pattern = '^M?M?M?$' >>> re.search(pattern, 'M') ① <_sre.SRE_Match object at 0x008EE090> >>> pattern = '^M?M?M?$' >>> re.search(pattern, 'MM') ② <_sre.SRE_Match object at 0x008EEB48> >>> pattern = '^M?M?M?$' >>> re.search(pattern, 'MMM') ③ <_sre.SRE_Match object at 0x008EE090> >>> re.search(pattern, 'MMMM') ④ >>>
M
にマッチする。しかし、2番目と3番目のM
にはマッチせず(しかしこれは省略可能なので問題ない)、そして文字列の末尾にマッチする。
M
にマッチする。しかし3番目のM
にはマッチせず(しかしこれは省略可能なので問題ない)、そして文字列の末尾にマッチする。
M
にマッチし、次に文字列の末尾にマッチする。
M
にマッチする。しかし文字列の終わりにはマッチしないので(まだマッチしていないM
があるから)、パターンはマッチせずNone
が返る。
>>> pattern = '^M{0,3}$' ① >>> re.search(pattern, 'M') ② <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MM') ③ <_sre.SRE_Match object at 0x008EE090> >>> re.search(pattern, 'MMM') ④ <_sre.SRE_Match object at 0x008EEDA8> >>> re.search(pattern, 'MMMM') ⑤ >>>
M
にマッチし、次に文字列の終わりにマッチする」と言っている。この0と3はどんな数値であっても構わない。もし1個以上3個以下のM
にマッチさせたいのであれば、M{1,3}
にすればよい。
M
にマッチし、次に文字列の末尾にマッチする。
M
にマッチし、次に文字列の末尾にマッチする。
M
にマッチし、次に文字列の末尾にマッチする。
M
にマッチする。しかし、文字列の末尾にはマッチしない。この正規表現は文字列の末尾の前に最高で3つまでのM
を許容するが、ここでは4つあるので、パターンはマッチせずNone
が返る。
それでは、ローマ数字の正規表現を拡張して10の位と1の位も扱えるようにしよう。次の例は10の位をチェックするものだ。
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)$' >>> re.search(pattern, 'MCMXL') ① <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MCML') ② <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MCMLX') ③ <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MCMLXXX') ④ <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MCMLXXXX') ⑤ >>>
M
にマッチし、次にCM
にマッチし、次にXL
にマッチし、次に文字列の末尾にマッチする。(A|B|C)
構文が「AかBかCのどれか1つだけにマッチする」というものだったことを思いだそう。ここではXL
にマッチしたので、XC
とL?X?X?X?
の選択肢は無視されて、文字列の末尾へ進む。MCMXL
は1940
をローマ数字で表したものだ。
M
にマッチし、次にCM
にマッチし、次にL?X?X?X?
にマッチする。L?X?X?X?
では、L
にマッチして選択的なX
をすべてスキップする。そして文字列の末尾に到達する。MCML
は1950
をローマ数字で表したものだ。
M
にマッチし、次にCM
にマッチし、次に省略可能なL
にマッチし、次に1つ目の省略可能なX
にマッチし、2つ目と3つ目の省略可能なX
は無視され、次に文字列の末尾にマッチする。MCMLX
は1960
をローマ数字で表したものだ。
M
にマッチし、次にCM
にマッチし、次に省略可能なL
にマッチし、次に3つすべての省略可能なX
にマッチし、次に文字列の末尾にマッチする。MCMLXXX
は1980
をローマ数字で表したものだ。
M
にマッチし、次にCM
にマッチし、次に省略可能なL
にマッチし、次に3つすべての省略可能なX
にマッチするが、文字列の末尾にはマッチしない。なぜなら、まだマッチしていないX
が残っているからだ。従ってパターン全体はマッチせず、None
を返す。MCMLXXX
は有効なローマ数字ではない。
1の位の表現もこれと同様だ。詳細は省いて結果だけを示そう。
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$'
それでは、{n,m}
構文を使った場合はどうなるのだろうか? 次の例ではこの新しい構文を使っている。
>>> pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$' >>> re.search(pattern, 'MDLV') ① <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MMDCLXVI') ② <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MMMDCCCLXXXVIII') ③ <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'I') ④ <_sre.SRE_Match object at 0x008EEB48>
M
にマッチし、D?C{0,3}
にマッチする。つまり、これは省略可能なD
と3つあり得るうちの0個のC
にマッチしている。続けて、省略可能なL
と3つありうるうちの0個のX
にマッチすることでL?X{0,3}
にマッチする。次に、省略可能なV
と3つあり得るうちの0個のI
にマッチすることでV?I{0,3}
にマッチする、そして、最後に文字列の末尾にマッチする。MDLV
は1555
をローマ数字で表したものだ。
M
にマッチし、D?C{0,3}
にD
と3つあり得るうちの1つのC
でマッチし、L?X{0,3}
にL
と3つあり得るうちの1つのX
でマッチし、V?I{0,3}
にV
と3つあり得るうちの1つのI
でマッチし、文字列の末尾にマッチする。MMDCLXVI
は2666
をローマ数字で表したものだ。
M
にマッチし、D?C{0,3}
にD
と3つあり得るうちの3つのC
でマッチし、L?X{0,3}
にL
と3つあり得るうちの3つのX
でマッチし、V?I{0,3}
にV
と3つあり得るうちの3つのI
でマッチし、文字列の末尾にマッチする。MMMDCCCLXXXVIII
は3888
をローマ数字で表したものであり、これは拡張された構文を用いずに表すことができる最も長いローマ数字だ。
M
にマッチし、次のD?C{0,3}
には、省略可能なD
を飛ばして3個あり得るうちの0個のC
でマッチし、次のL?X{0,3}
には、省略可能なL
を飛ばして3個あり得るうちの0個のX
でマッチし、次のV?I{0,3}
には、省略可能なV
を飛ばして3個あり得るうちの1個のI
でマッチし、文字列の末尾にマッチする。おおー。
もし初挑戦でこれを全て理解できたのなら、私が初めて学んだときよりも上手くやっているよ。さて今度は、他の人が書いた正規表現を読む時のことを考えてみよう。それも、巨大なプログラムのとりわけ重要な関数の中ほどあたりにあるやつを。あるいは、自分自身で書いた正規表現を何ヶ月か後に見かえすときのことを想像してもいい。私の経験を言えば、とても読めたものではなかったね。
今度は、正規表現をより保守しやすくしてくれる構文について学ぼう。
⁂
これまでは、私が「コンパクトな」正規表現と呼んでいるものを扱ってきた。以上の例を見て分かったように、このような正規表現は読み難く、たとえその内容を一旦は理解できたとしても、6ヶ月後にまた理解できる保証が無いような代物だった。ここで本当に必要なものはインラインのドキュメントだ。
Pythonでは冗長な正規表現と呼ばれるものを使ってこれを行うことができる。冗長な正規表現はコンパクトな正規表現とは2つの点で異なる:
#
という文字から始まり、その行の終わりまで続く。これはソースコードではなく複数行文字列の中にあるコメントだが、同じように機能する。
これは実際の例を見ればよく分かる。もう一度いままでのコンパクトな正規表現を見てみよう。そして、それを冗長な正規表現にするのだ。この例はそのやり方を示している。
>>> pattern = ''' ^ # beginning of string M{0,3} # thousands - 0 to 3 Ms (CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs), # or 500-800 (D, followed by 0 to 3 Cs) (XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs), # or 50-80 (L, followed by 0 to 3 Xs) (IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is), # or 5-8 (V, followed by 0 to 3 Is) $ # end of string ''' >>> re.search(pattern, 'M', re.VERBOSE) ① <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MCMLXXXIX', re.VERBOSE) ② <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'MMMDCCCLXXXVIII', re.VERBOSE) ③ <_sre.SRE_Match object at 0x008EEB48> >>> re.search(pattern, 'M') ④
re.VERBOSE
を渡す必要があるということだ。re.VERBOSE
はre
モジュールで定義される定数であり、この定数はパターンを冗長な正規表現として扱わなければならないことを知らせる。見ての通り、このパターンはたくさんの空白(全て無視される)と、いくつかのコメント(全て無視される)を含んでいる。空白やコメントを無視してしまえば、これは前の節で見た正規表現とまったく同じだが、ずっと読みやすいものになっている。
M
にマッチし、次にCM
にマッチし、次にL
と、3つあり得るうちの3つのX
にマッチし、次にIX
にマッチし、次に文字列の末尾にマッチする。
M
にマッチし、次にD
と、3つあり得るうちの3つのC
にマッチし、次にL
と、3つあり得るうちの3つのX
にマッチし、次にV
と、3つあり得るうちの3つのI
にマッチし、次に文字列の末尾にマッチする。
re.VERBOSE
フラグが与えられていないからだ。従って、re.search
関数はこのパターンをコンパクトな正規表現として扱い、空白を無視せず、#も文字通りのものと解釈する。Pythonは冗長な正規表現であるかどうかを自動で判定してはくれない。明示的に冗長な正規表現だと宣言しない限り、Pythonはすべての正規表現をコンパクトな正規表現とみなすのだ。
⁂
これまでは、パターン全体がマッチするものばかりを扱ってきた。パターンはマッチするかしないかのどちらかしかなかった。しかし、正規表現はそれよりももっと強力なものだ。正規表現がマッチするときは、その特定の一部分を取り出すことができる。どこで何がマッチしたのかを知ることができるのだ。
この例は、私が現実に出会った問題から着想を得ている。これまた私の前の仕事からのものだ。問題となったのはアメリカの電話番号のパースだった。その時、顧客が求めてきたのは、電話番号を自由な形式で(1つのフィールドに)入力できるようにしつつ、そこから市外局番・局番・残りの番号、そしてオプションとして内線番号を取り出して会社のデータベースに別々に格納することだった。私はWeb上を探し回り、この処理を行うと主張している正規表現の例をたくさん見つけたが、ライセンス的に使えるものが1つもなかった。
上手く処理できるようにしなければならなかった電話番号は次のようなものだ:
800-555-1212
800 555 1212
800.555.1212
(800) 555-1212
1-800-555-1212
800-555-1212-1234
800-555-1212x1234
800-555-1212 ext. 1234
work 1-(800) 555.1212 #1234
パターンが多すぎる! いずれの場合においても、市外局番は800
で、局番は555
で、残りの電話番号は1212
であることを読み取れる必要がある。内線番号が存在する場合は、内線番号が1234
であることも読み取れなくてはならない。
それでは電話番号をパースする方法を構築しよう。この例はその最初の一歩だ。
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$') ① >>> phonePattern.search('800-555-1212').groups() ② ('800', '555', '1212') >>> phonePattern.search('800-555-1212-1234') ③ >>> phonePattern.search('800-555-1212-1234').groups() ④ Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'groups'
(\d{3})
にマッチする。\d{3}
というのは何だろう? \d
は任意の数字(0
から9
まで)を意味している。{3}
は「ちょうど3つの数字にマッチする」という意味で、前に見た{n,m}
構文の一種だ。これを括弧の中に入れているのは「ちょうど3桁の数字にマッチさせ、マッチしたものを後で参照できるようにグループとして覚えておいてくれ」という意味だ。次にハイフンにマッチする。その次はまた別のちょうど3桁の数字のグループにマッチする。次にハイフンにマッチする。その次はまた別のちょうど4桁の数字にマッチする。次に文字列の終わりにマッチする。
search()
メソッドが返したオブジェクトにあるgroups()
メソッドを使う。このメソッドは正規表現で定義したグループがいくつあったとしても、それらをタプルで返してくれる。この場合は、3つのグループを定義している。そのうちの1つは3桁の数字で、もう1つは3桁の数字、最後の1つは4桁の数字だ。
search()
メソッドとgroups()
メソッドを製品コードで「連鎖」させてはいけない理由を表している。正規表現がマッチしなかった場合には、search()
メソッドは正規表現のマッチオブジェクトではなく、None
を返す。None.groups()
を呼び出そうとすると、分かりきったことだが、例外が送出されることになる。None
はgroups()
というメソッドを持っていないのだ(もちろん、コードの深いところでこの例外が出た場合は少し分かりづらくなる。そう、ここでは私は自分の経験に基づいてお話している)。
>>> phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})-(\d+)$') ① >>> phonePattern.search('800-555-1212-1234').groups() ② ('800', '555', '1212', '1234') >>> phonePattern.search('800 555 1212 1234') ③ >>> >>> phonePattern.search('800-555-1212') ④ >>>
groups()
メソッドは4つの要素からなるタプルを返す。正規表現に4つの記憶するグループを定義しているからだ。
次の例は、正規表現の個々の部分のあいだにある区切り文字を扱う正規表現を示している。
>>> phonePattern = re.compile(r'^(\d{3})\D+(\d{3})\D+(\d{4})\D+(\d+)$') ① >>> phonePattern.search('800 555 1212 1234').groups() ② ('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212-1234').groups() ③ ('800', '555', '1212', '1234') >>> phonePattern.search('80055512121234') ④ >>> >>> phonePattern.search('800-555-1212') ⑤ >>>
\D+
にマッチしている。これは一体何だろう? \D
は数字を除く全ての文字にマッチし、そして+
は「1回以上」を意味している。つまり\D+
は1つ以上の数字ではない文字にマッチする。異なる区切り文字にもマッチできるように、ハイフンの代わりにこれを使っているのだ。
-
の代わりに\D+
を使うということは、ハイフンではなくスペースで分割された電話番号にもマッチすることを意味している。
次の例は区切り文字のない電話番号を扱う正規表現を示している。
>>> phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$') ① >>> phonePattern.search('80055512121234').groups() ② ('800', '555', '1212', '1234') >>> phonePattern.search('800.555.1212 x1234').groups() ③ ('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212').groups() ④ ('800', '555', '1212', '') >>> phonePattern.search('(800)5551212 x1234') ⑤ >>>
+
を*
に変更したことだけだ。\D+
を電話番号の各部分の区切りとして使う代わりに、今度は\D*
にマッチさせるのだ。+
は「1回以上」を意味することを覚えているだろうか? *
は「0回以上」を意味しているのだ。だから、たとえ区切り文字がまったく使われていない電話番号であったとしても、これでパースできるようになっているはずだ。
800
) のグループにマッチし、次に0個の数字ではない文字にマッチし、次に記憶しておく3桁の数字 (555
) のグループにマッチし、次に0個の数字ではない文字にマッチし、次に記憶しておく4桁の数字 (1212
) のグループにマッチし、次に0個の数字ではない文字にマッチし、次に記憶しておく任意桁の数字 (1234
) にマッチし、次に文字列の末尾にマッチする。
x
があるものでも対応できる。
groups()
メソッドは4つの要素をもったタプルを返してくれるし、見つからなければ4番目の要素は単に空の文字列になる。
次の例は電話番号の前にある文字を扱い方を示している。
>>> phonePattern = re.compile(r'^\D*(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$') ① >>> phonePattern.search('(800)5551212 ext. 1234').groups() ② ('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212').groups() ③ ('800', '555', '1212', '') >>> phonePattern.search('work 1-(800) 555.1212 #1234') ④ >>>
\D*
(0個以上の数字ではない文字)にマッチさせている点が異なる。これらの数字でない文字は記憶されないことに注意しよう。もしそれらが見つかっても単に読み飛ばされてしまう。そして市外局番に行き着くと、それ以降の数字が記憶され始めるのだ。
\D*
にマッチする)
800
)にマッチし、次に1つの数字ではない文字にマッチし(ハイフン)、次に記憶される3桁の数字のグループ(555
)にマッチし、次に1つの数字ではない文字にマッチし(ハイフン)、次に記憶される4桁の数字のグループ(1212
)にマッチし、次に0個の数字ではない文字にマッチし、次に記憶される0桁の数字のグループにマッチし、次に文字列の末尾にマッチする。
1
があるが、市外局番の前に来るものは全て数字でない文字(\D*
)だと想定していたからだ。あーもう!
少し落ち着こう。今までの正規表現は文字列の頭から全部マッチさせてきた。しかし、現在では文字列の先頭に無視したいものがいくつも存在しうるということが分かっている。全体をマッチさせてそれらを読み飛ばすのではなく、違うやり方をしてみよう。つまり、明示的に文字列の先頭にはマッチさせないようにするのだ。このやり方を次の例で示す。
>>> phonePattern = re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$') ① >>> phonePattern.search('work 1-(800) 555.1212 #1234').groups() ② ('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212') ③ ('800', '555', '1212', '') >>> phonePattern.search('80055512121234') ④ ('800', '555', '1212', '1234')
^
が無いことに注意しよう。もう文字列の先頭にはマッチさせないのだ。正規表現を入力全体とマッチさせる必要はどこにもない。正規表現エンジンが入力文字列にマッチし始める位置を頑張って見つけ出し、そこからマッチを行ってくれるのだ。
正規表現が瞬く間に手に負えないものになってしまうことが分かっただろうか? 今までに繰り返してきたものをどれでも良いので少し見て欲しい。ある正規表現とその次のバージョンの正規表現を区別できるだろうか?
あなたがまだ最終バージョンの正規表現を理解しているうちに(これが最終的な答えだ。もしこれで扱えない例が見つかったとしても、私はそんなことは知りたくないね)、どうしてこのように構成したのかを忘れないうちに冗長な正規表現として書いておこう。
>>> phonePattern = re.compile(r''' # don't match beginning of string, number can start anywhere (\d{3}) # area code is 3 digits (e.g. '800') \D* # optional separator is any number of non-digits (\d{3}) # trunk is 3 digits (e.g. '555') \D* # optional separator (\d{4}) # rest of number is 4 digits (e.g. '1212') \D* # optional separator (\d*) # extension is optional and can be any number of digits $ # end of string ''', re.VERBOSE) >>> phonePattern.search('work 1-(800) 555.1212 #1234').groups() ① ('800', '555', '1212', '1234') >>> phonePattern.search('800-555-1212') ② ('800', '555', '1212', '')
⁂
今までの例は、正規表現ができることの氷山の一角にすぎない。言い換えれば、たとえ今あなたがこれらに完全に圧倒されているとしても、信じて欲しいが、こんなものはまだ序の口なのだ。
今あなたは、以下のテクニックを身につけているはずだ:
^
は文字列の先頭にマッチする。
$
は文字列の末尾にマッチする。
\b
は単語境界にマッチする。
\d
は任意の数字にマッチする。
\D
は数字以外の任意の文字にマッチする。
x?
はx
と言う文字に任意でマッチする(言い換えると、x
が0回または1回現れるものにマッチする)。
x*
はx
が0回以上現れるものにマッチする。
x+
はx
が1回以上現れるものにマッチする。
x{n,m}
はx
がn
回以上、m
回以下現れるものにマッチする。
(a|b|c)
はa
, b
, c
のどれか一つだけにマッチする。
(x)
は一般に記憶されるグループのことだ。re.search
メソッドが返したオブジェクトにあるgroups()
メソッドを使うことで、マッチした値を取得できる。
正規表現は非常に強力だが、すべての問題に対する正しい答えではない。正規表現がどんなときに適切で、どんなときに問題を解決してくれ、どんなときに初めの問題よりも多くの問題を引き起こしてしまうのかを、十分に学ぶ必要がある。
© 2001– Mark Pilgrim
© Fukada, Fujimoto(日本語版)