正規表現
正規表現(regex と略される)は、テキスト内の検索パターンを指定する文字のシーケンスです。[...] 文字列に対する「検索」または「検索と置換」操作、あるいは入力検証のために文字列検索アルゴリズムによって使用されます。
import reで regex モジュールをインポートします。re.compile()関数で Regex オブジェクトを作成します。(raw string を使用することを忘れないでください。)- 検索したい文字列を Regex オブジェクトの
search()メソッドに渡します。これによりMatchオブジェクトが返されます。 - Match オブジェクトの
group()メソッドを呼び出して、実際に一致したテキストの文字列を取得します。
Python のすべての regex 関数は re モジュール内にあります。
# 正規表現操作のために re モジュールをインポート
import re
Regex 記号
| 記号 | 一致するもの |
|---|---|
? | 直前のグループの 0 回または 1 回。 |
* | 直前のグループの 0 回以上。 |
+ | 直前のグループの 1 回以上。 |
{n} | 直前のグループのちょうど n 回。 |
{n,} | 直前のグループの n 回以上。 |
{,m} | 直前のグループの 0 回から m 回。 |
{n,m} | 直前のグループの少なくとも n 回、最大 m 回。 |
{n,m}? または *? または +? | 直前のパターンの非貪欲マッチを実行します。 |
^spam | 文字列が spam で始まる必要があることを意味します。 |
spam$ | 文字列が spam で終わる必要があることを意味します。 |
. | 改行文字を除く任意の 1 文字。 |
\d, \w, および \s | それぞれ、数字、単語、またはスペース文字。 |
\D, \W, および \S | それぞれ、数字、単語、またはスペース以外のすべて。 |
[abc] | ブラケット間にある任意の文字(a、b、のような)。 |
[^abc] | ブラケット間にない任意の文字。 |
マッチング regex オブジェクト
# re.compile(): regex パターンオブジェクトを作成します (エスケープを避けるために raw string r'' を使用)
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # パターン:3 桁 -3 桁 -4 桁
mo = phone_num_regex.search('My number is 415-555-4242.') # パターンを検索
print(f'Phone number found: {mo.group()}') # group() は一致したテキストを返します
Phone number found: 415-555-4242
丸括弧によるグループ化
# 丸括弧はグループを作成します:group(1) は最初のグループを返し、group(2) は 2 番目を返します
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') # 丸括弧内に 2 つのグループ
mo = phone_num_regex.search('My number is 415-555-4242.')
mo.group(1) # 最初のグループを返します:'415'
'415'
mo.group(2)
'555-4242'
mo.group(0)
'415-555-4242'
mo.group()
'415-555-4242'
ログインしてこのクイズに回答し、学習の進捗を追跡できます
group() を呼び出すと、何が返されますか?すべてのグループを一度に取得するには、groups() メソッドを使用します。
# groups(): すべてのグループのタプルを返します
mo.groups() # ('415', '555-4242') を返します
('415', '555-4242')
area_code, main_number = mo.groups()
print(area_code)
415
print(main_number)
555-4242
パイプによる複数グループ
| 文字を使用して、多くの式のいずれかに一致させたい場所を指定できます。
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'
パイプを使用して、正規表現の一部として複数のパターンのいずれかに一致させることもできます。
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'
アスタリスクによるゼロ回以上のマッチング
* (アスタリスク) は「ゼロ回以上一致」を意味します。アスタリスクの前のグループは、テキスト内で何度でも出現できます。
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 Batwowowowoman')
mo3.group()
'Batwowowowoman'
プラスによる 1 回以上のマッチング
+ (プラス) は「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 Batwowowowoman')
mo2.group()
'Batwowowowoman'
mo3 = bat_regex.search('The Adventures of Batman')
mo3 is None
True
波括弧による特定回数のマッチング
グループを特定の回数繰り返したい場合は、正規表現内のグループの後に波括弧で囲んだ数値を続けます。
ha_regex = re.compile(r'(Ha){3}')
mo1 = ha_regex.search('HaHaHa')
mo1.group()
'HaHaHa'
mo2 = ha_regex.search('Ha')
mo2 is None
True
1 つの数値の代わりに、波括弧の間に最小値と最大値を指定して範囲を指定できます。たとえば、正規表現 (Ha){3,5} は ‘HaHaHa’、‘HaHaHaHa’、および ‘HaHaHaHaHa’ に一致します。
ha_regex = re.compile(r'(Ha){2,3}')
mo1 = ha_regex.search('HaHaHaHa')
mo1.group()
'HaHaHa'
貪欲マッチと非貪欲マッチ
Python の正規表現はデフォルトで貪欲です。曖昧な状況では、可能な限り長い文字列に一致しようとします。波括弧の非貪欲バージョン(可能な限り短い文字列に一致)は、閉じ波括弧の後に疑問符を付けたものです。
greedy_ha_regex = re.compile(r'(Ha){3,5}')
mo1 = greedy_ha_regex.search('HaHaHaHaHa')
mo1.group()
'HaHaHaHaHa'
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
mo2.group()
'HaHaHa'
ログインしてこのクイズに回答し、学習の進捗を追跡できます
+ の代わりに _ を使用する_?、+?、{3,5}?) の後に ? を追加するfindall() メソッド
findall() メソッドは、検索された文字列内のすべての一致の文字列を返します。
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # グループなし
phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']
独自の文字クラスの作成
角括弧を使用して独自の文字クラスを定義できます。たとえば、文字クラス [aeiouAEIOU] は、小文字と大文字の両方の母音に一致します。
vowel_regex = re.compile(r'[aeiouAEIOU]')
vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']
ハイフンを使用して文字または数字の範囲を含めることもできます。たとえば、文字クラス [a-zA-Z0-9] は、すべて小文字、すべて大文字、および数字に一致します。
文字クラスの開始ブラケットの直後にキャレット文字 (^) を配置すると、文字クラスに含まれていないすべての文字に一致する否定文字クラスを作成できます。
consonant_regex = re.compile(r'[^aeiouAEIOU]')
consonant_regex.findall('Robocop eats baby food. BABY FOOD.')
['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']
キャレットとドル記号の文字
正規表現の先頭でキャレット記号
^を使用して、検索テキストの先頭で一致が発生する必要があることを示すことができます。同様に、正規表現の末尾にドル記号
$を配置すると、文字列がこの正規表現パターンで終了する必要があることを示します。そして、
^と$を一緒に使用して、文字列全体が正規表現に一致する必要があることを示すことができます。
正規表現文字列 r'^Hello' は ‘Hello’ で始まる文字列に一致します。
begins_with_hello = re.compile(r'^Hello')
begins_with_hello.search('Hello world!')
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
begins_with_hello.search('He said hello.') is None
True
正規表現文字列 r'\d\$' は、0 から 9 の数値で終わる文字列に一致します。
whole_string_is_num = re.compile(r'^\d+$')
whole_string_is_num.search('1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
whole_string_is_num.search('12345xyz67890') is None
True
whole_string_is_num.search('12 34567890') is None
True
ワイルドカード文字
正規表現の . (ドット) 文字は、改行を除く任意の文字に一致します。
at_regex = re.compile(r'.at')
at_regex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']
ドットスターによるすべての一致
name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = name_regex.search('First Name: Al Last Name: Sweigart')
mo.group(1)
'Al'
mo.group(2)
'Sweigart'
.* は貪欲モードを使用します。可能な限り多くのテキストに一致させようとします。あらゆるテキストを非貪欲に一致させるには、ドット、スター、疑問符 (.*?) を使用します。疑問符は Python に非貪欲に一致させるように指示します。
non_greedy_regex = re.compile(r'<.*?>')
mo = non_greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man>'
greedy_regex = re.compile(r'<.*>')
mo = greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man> for dinner.>'
ドット文字による改行の一致
ドットスターは改行を除くすべてに一致します。re.compile() の 2 番目の引数として re.DOTALL を渡すことで、ドット文字が改行文字を含むすべての文字に一致するようにできます。
no_newline_regex = re.compile('.*')
no_newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.'
newline_regex = re.compile('.*', re.DOTALL)
newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.\nProtect the innocent.\nUphold the law.'
大文字・小文字を区別しないマッチング
正規表現を大文字・小文字を区別しないようにするには、re.compile() の 2 番目の引数として re.IGNORECASE または re.I を渡すことができます。
robocop = re.compile(r'robocop', re.I)
robocop.search('Robocop is part man, part machine, all cop.').group()
'Robocop'
robocop.search('ROBOCOP protects the innocent.').group()
'ROBOCOP'
robocop.search('Al, why does your programming book talk about robocop so much?').group()
'robocop'
sub() メソッドによる文字列の置換
Regex オブジェクトの sub() メソッドには 2 つの引数が渡されます。
- 最初の引数は、一致したものを置き換える文字列です。
- 2 番目は、正規表現の文字列です。
sub() メソッドは、置換が適用された文字列を返します。
names_regex = re.compile(r'Agent \w+')
names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'
ログインしてこのクイズに回答し、学習の進捗を追跡できます
sub() メソッドは何をしますか?複雑な Regex の管理
re.compile() 関数に、正規表現文字列内の空白文字とコメントを無視するように指示するには、「verbose モード」を有効にできます。これは、re.compile() の 2 番目の引数として変数 re.VERBOSE を渡すことで実行できます。
このとき、次のような読みにくい正規表現の代わりに:
phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')
次のように、コメント付きで正規表現を複数行にわたって記述できます。
phone_regex = re.compile(r'''(
(\d{3}|\(\d{3}\))? # 市外局番
(\s|-|\.)? # 区切り文字
\d{3} # 最初の 3 桁
(\s|-|\.) # 区切り文字
\d{4} # 最後の 4 桁
(\s*(ext|x|ext.)\s*\d{2,5})? # 内線番号
)''', re.VERBOSE)
ログインしてこのクイズに回答し、学習の進捗を追跡できます
re.compile() に渡された re.VERBOSE は何を行いますか?