毎日ちょっとPython ~ 14日目 ~
おひさしぶりです。二週間ですね...積み上げだいじ
前回のおさらい
1. import reで正規表現モジュールをインポートする
2. re.compile()関数を呼び出しRegexオブジェクトを生成する(raw文字列を使う)。
3. Regexオブジェクトのsearch()メソッドに、検索対象の文字列を渡すと、Matchオブジェクトを返す。
4. Matchオブジェクトの group() メソッドを呼び出し、実際にマッチした文字列を取得する。
丸カッコを用いたグルーピング
電話番号を市外局番とそれ以外に分けて取得するみたいなことできます。そうするには、(\d\d\d)-(\d\d\d-\d\d\d\d)みたいな感じにするとそれぞれ一つ目二つ目みたいな感じで取得することができます。
>>> phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> mo = phone_num_regex.search('私の電話番号は415-555-4242です。')
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'
>>> mo.group(0) # または mo.group()でもイケる
'415-555-4242'
それぞれ、
0 - 対象となる正規表現にマッチした全体
以降の数字 - () でくくられた部分でマッチしたものが左から順番に入る
感じかな。
一気に表示させるには、groups()という複数形のメソッドでいけます。返す値はタプル型になります。
>>> mo.groups()
('415', '555-4242')
>>> area_code,main_number = mo.groups()
>>> print(area_code)
415
>>> print(main_number)
555-4242
正規表現で()を検索させたい場合はどうしましょうかね?()をバックスラッシュでエスケープすればOKです。
>>> phone_num_regex = re.compile(r'(\(\d\d\d\)) (\d\d\d-\d\d\d\d)')
>>> mo = phone_num_regex.search('私の電話番号は (415) 555-4242 です')
>>> mo.group(1)
'(415)'
>>> mo.group(2)
'555-4242'
re.compile()に渡すraw文字列のなかで、\( や \) と書けば本来の丸カッコの文字とマッチするようになる。
縦線を使って複数のグループとマッチする
| 文字は「縦線」と呼ばれます。(パイプとかじゃなかったっけ?)複数のパターンのうちの一つとマッチすればみたいな感じになります。
>>> hero_regex = re.compile(r'Batman|Tina Fey')
>>> mo1 = hero_regex.search('Batman and Tina Fey.')
>>> mo1.group()
'Batman'
>>> mo2 = hero_regex.search('Tina Fey and Batman')
>>> mo2.group()
'Tina Fey'
上記のように二つにマッチするような文字列の場合、最初に出威厳したほうがMatchオブジェクトして返ります
まぁあとは、一部分だけ共通していてそれ以外の部分が違うような場合に有効です。
>>> bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
>>> mo = bat_regex.search('Batmobile lost a wheel')
>>> mo.group()
'Batmobile'
>>> mo.group(1)
'mobile'
疑問符を用いた任意のマッチ
マッチしてもしなくてもよい任意のマッチングをしたい場合には ? が有効です。
>>> bat_regex = re.compile(r'Bat(wo)?man')
>>> mo1 = bat_regex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo2 = bat_regex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'
この辺はJSとほとんどルールは一緒ですね。
アスタリスクを用いた0回以上のマッチ
直前の文字/数字/グループがいくらあってもOK(0個でもOK)
>>> bat_regex = re.compile(r'Bat(wo)*man')
>>> mo1 = bat_regex.search('The Adventures of Batman')
>>> mo1.group()
'Batman'
>>> mo2 = bat_regex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'
>>> mo3 = bat_regex.search('The Adventures of Batwowowowowowowowowowowowowowoman')
>>> mo3.group()
'Batwowowowowowowowowowowowowowoman'
プラスを用いた1回以上のマッチ
こっちは1個以上ないとだめ
>>> bat_regex = re.compile(r'Bat(wo)+man')
>>> mo1 = bat_regex.search('The Adventures of Batwoman')
>>> mo1.group()
'Batwoman'
>>> mo2 = bat_regex.search('The Adventures of Batwowowowowowowowowowowowowowoman')
>>> mo2.group()
'Batwowowowowowowowowowowowowowoman'
>>> mo3 = bat_regex.search('The Adventures of Batman')
>>> mo3 == None
True
波カッコを用いて繰り返し回数を指定する
グループの繰り返し出現回数を指定したいときには、波カッコの中に回数を指定します。
例えば、 (Ha){3}という正規表現は 'HaHaHa' にはマッチしますか、'HaHa'にはマッチしません。
で、{3,5}とすると 'HaHaHa', 'HaHaHaHa','HaHaHaHaHa' にマッチします。
片方の数を省略すると、最小値と最大値を指定しないことになります。
(Ha){3,} -> 3回以上出現する場合にマッチ
(Ha){,5} -> 5回以下の場合にマッチ
正規表現を短く書くのに役立ちます。
貪欲マッチと非貪欲マッチ
'HaHaHaHaHa' という文字列に対して、(Ha){3,5}というパターンは、Haが3回、4回、5回のいずれの場合にもマッチします。ただ、Pythonの正規表現はデフォルトでは「貪欲(greedy)」にマッチし、そのなかの最大数をとります。一方、閉じかっこの後に疑問符?をつけると「非貪欲」なマッチを意味し、最も短いものにマッチするようになる。
>>> greedy_Ha_regex = re.compile(r'(Ha){3,5}')
>>> mo1 = greedy_Ha_regex.search('HaHaHaHaHaHa')
>>> mo1.group()
'HaHaHaHaHa'
>>> non_greedy_Ha_regex = re.compile(r'(Ha){3,5}?')
>>> mo2 = non_greedy_Ha_regex.search('HaHaHaHaHaHa')
>>> mo2.group()
'HaHaHa'
最後に
貪欲マッチとかあるんですね。Javascriptはどちらなんでしょうかね。