正規表現
正規表現について
正規表現(Regular Expression) は、特殊な構文をパターンとして使用して、他の文字列または文字列のセットを検索したり合致判断や抽出するための特殊な文字シーケンスです。
UNIX系OSでも古くから利用されているものです。
Pythonでも標準モジュール re で、正規表現を完全にサポートします。
正規表現のパターンが対象とする文字列は、特殊文字などが含まれていることがあるため、r'...'としてraw文字列とすることに留意してください。
正規表現のメタ文字
正規表現では、メタ文字と呼ばれる特殊な記法を使ってパターンを作ります。
[]はScrapBox の仕様上の制限のため全角文字で表記していますが、
実際には半角記号の[]です。
table:正規表現でのメタ文字
文字 説明 記述例 マッチする マッチしない
. 任意の一文字 a.c abc, acc, aac abbc, accc
^ 文字列の先頭 ^abc abcdef defabc
$ 文字列の末尾 abc$ defabc abcdef
* 0回以上の繰り返し ab* a, ab, abb, abbb aa, bb
+ 1回以上の繰り返し ab+ ab, abb, abbb a, aa, bb
? 0回または1回 ab? a, ab abb
{m} m回の繰り返し a{3} aaa a, aa, aaaa
{m,n} m〜n回の繰り返し a{2, 4} aa, aaa, aaaa a, aaaaa
[] 集合 [a-c] a, b, c d, e, f
| (縦線) 和集合(または) a | b a, b c, d
() グループ化 (abc)+ abc, abcabc a, ab, abcd
table: 正規表現での特殊文字
文字 説明 別記法
\d 任意の数字 [0-9]
\D 任意の数字以外 [^0-9]
\s 任意の空白文字 [\t\n\r\f\v]
\S 任意の空白文字以外 [^\t\n\r\f\v]
\w 任意の英数字 [a-xA-Z0-9_]
\W 任意の英数字以外 [\a-xA-Z0-9_]
\A 文字列の先頭 ^
\Z 文字列の末尾 $
\b 空文字列にマッチ。それが単語の先頭か末尾 のときのみ
\B 空文字列にマッチ。それが単語の先頭か末尾 でない ときのみ
\b の反対の意味
貪欲と非貪欲について
正規表現のメタ文字で使用される *(0もしくはそれ以上)、+ (1もしくはそれ以上)はとてもよく使うものです。デフォルトではこれらのメタ文字はできるだけ長い文字列がマッチするように欲張り(greedy:貪欲)ます。
このメタ文字には、*? や +? というように ? を後につけることができ、この場合は欲張らず(non-greedy: 非貪欲) にできるだけ短い文字列にマッチするようになります。
メタ文字の?(0もしくは1)と同じで間違いやすいので注意しましょう。
日本語に対しての正規表現
table: 日本語に対しての正規表現
パターン 表記 説明
[ぁ-ん] 任意のひらがな
[ァ-ン] 任意のカタカナ
[ぁ-んァ-ン] 任意のひらがなとカタカナ
[一-龥] 任意の漢字
reモジュールの関数
re モジュールでは次の関数が定義されています。
match() : 文字列の先頭がパターンにマッチするかを調べる
fullmatch() : 文字列の先頭がパターンにマッチするかを調べる
search(): 先頭に限らずパターンにマッチするかを調べる
findall(): マッチする部分すべてをリストで返す
finditer(): マッチする部分すべてをイテレータで返す
sub()、subn(): マッチした部分を置換する
split(): パターンで文字列を分割する
compile():正規表現オブジェクトをコンパイルする
match() の使用例
stringの先頭が正規表現 pattern にマッチすればマッチオブジェクトが返されます。
マッチしなかったときは None が返されます。
matchObj = match(pattern, string, flag)
pattern: 正規表現のパターンを指定
string: 検索対象の文字列
flag: REフラグ (詳しくは後述します)
matchObj: マッチオブジェクトが返されます。マッチしなかったときは None を返します
code: re_match_demo.py
import re
text='If you want to using regular expression, you must import the re module'
patterns=[
'epression',
'you',
'^If.*to',
'module$',
]
print(text)
for ptn in patterns:
x = re.match(ptn, text)
print(f'Pattern: {ptn}')
if x != None:
print(f'group: {x.group()}')
print(f'start: {x.start()}')
print(f' end: {x.end()}')
print(f' span: {x.span()}')
実行してみましょう。
この例では、検索対象の文字列にも you があり、re.match() のパターンに you を与えていますが、マッチしていません。
code: bash
$ python re_match_demo.py
If you want to using regular expression, you must import the re module
Pattern: epression
Pattern: you
Pattern: ^If.*to
group: If you want to
start: 0
end: 14
span: (0, 14)
Pattern: module$
これは、パターンに you の前後に文字があってもよいようにメタ文字が定義れていないため、文字列全体が you ではないので、マッチしなかったからです。
余談
正規表現のプログラムでは Python 3.8 から導入された代入式が便利になります。
fullmatch() の使用例
文字列全体が正規表現 pattern にマッチすればマッチオブジェクトが返されます。
マッチしなかったときは None が返されます。
オプションの pos および endpos 引数は search() メソッドのものと同じ意味で、
対象の文字列の範囲を限定することができます。
matchObj = fullmatch(pattern, [start, [ end]])
pattern: 正規表現のパターンを指定
start: 文字列での検索開始位置
end: 文字列の検索終了位置
matchObj: マッチオブジェクトが返されます。マッチしなかったときは None を返します
code: re_fullmatch_demo.p
import re
text='If you want to using regular expression, you must import the re module'
patterns=[
'epression',
'you',
'^If.*to.*',
'module$',
]
print(text)
for ptn in patterns:
x = re.fullmatch(ptn, text)
print(f'Pattern: {ptn}')
if x != None:
print(f'group: {x.group()}')
print(f'start: {x.start()}')
print(f' end: {x.end()}')
print(f' span: {x.span()}')
実行してみましょう。
この例では、行全体にマッチするのパターンは3つめのものしかありません。
code: bash
$ python re_fullmatch_demo.py
If you want to using regular expression, you must import the re module
Pattern: epression
Pattern: you
Pattern: ^If.*to.*
group: If you want to using regular expression, you must import the re module
start: 0
end: 70
span: (0, 70)
Pattern: module$
search() の使用例
stringを左から右に正規表現 pattern にマッチするか調べていき、マッチすればマッチオブジェクトが返されます。マッチしなかったときは None が返されます。
matchObj = search(pattern, string, flag)
pattern: 正規表現のパターンを指定
string: 検索対象の文字列
flag: REフラグ (詳しくは後述します)
matchObj: マッチオブジェクトが返されます。マッチしなかったときは None を返します
re.search() で指定したパターンにマッチした文字列は、matchObj.group() で参照することができますl.
code: re_search_demo.py
import re
text='If you want to using regular expression, you must importe the re module'
patterns=[
'epression',
'you',
'^If.*to',
'module$',
]
print(text)
for ptn in patterns:
x = re.search(ptn, text)
print(f'Pattern: {ptn}')
if x != None:
print(f'group: {x.group()}')
print(f'start: {x.start()}')
print(f' end: {x.end()}')
print(f' span: {x.span()}')
実行してみましょう。
code: bash
$ python re_search_demo.py
If you want to using regular expression, you must import the re module
Pattern: epression
Pattern: you
group: you
start: 3
end: 6
span: (3, 6)
Pattern: ^If.*to
group: If you want to
start: 0
end: 14
span: (0, 14)
Pattern: module$
group: module
start: 64
end: 70
span: (64, 70)
re.match() のときとは違って、今度は you がマッチしています。
しかし、この例の検索対象の文字列には you が2回でてきますが、re.search() では、はじめにマッチした文字列の情報しか取得できません。
findall() の使用例
string を正規表現 pattern にマッチするすべての重複しないマッチを文字列のリストとして返します。見つかった順にリストは追加されていきます。
パターンにグループがあるときは、グループのリストを返します。
パターンに複数のグループがあるきは、タプルのリストになります。
マッチしない場合も結果に含まれます。
matchObj = findall(pattern, string, flag)
pattern: 正規表現のパターンを指定
string: 検索対象の文字列
flag: REフラグ (詳しくは後述します)
matchObj: マッチオブジェクトが返されます。マッチしなかったときは None を返します
code: re_findall_demo.py
import re
text='If you want to using regular expression, you must import the re module'
patterns=[
'epression',
'you',
'^If.*to',
'module$',
]
print(text)
for ptn in patterns:
x = re.findall(ptn, text)
print(f'Pattern: {ptn} {x}')
code: bash
$ python re_findall_demo1.py
If you want to using regular expression, you must import the re module
Pattern: epression []
finditer() の使用例
string を正規表現 pattern にマッチする、すべての重複しないマッチをマッチオブジェクトのイテレータとして返します。
文字列は左から右に調べられ、見つかった順にマッチオブジェクトは返されます。
パターンにグループがあるときは、グループのリストを返します。
matchObj = finditr(pattern, string, flag)
pattern: 正規表現のパターンを指定
string: 検索対象の文字列
flag: REフラグ (詳しくは後述します)
matchObj: マッチオブジェクトが yield で返されます。
code: re_finditer_demo.py
import re
text='If you want to using regular expression, you must import the re module'
patterns=[
'epression',
'you',
'^If.*to',
'module$',
]
print(text)
for ptn in patterns:
print(f'Pattern: {ptn}')
for match in re.finditer(ptn, text):
print(f'group: {match.group()}')
print(f'start: {match.start()}')
print(f' end: {match.end()}')
print(f' span: {match.span()}')
code: bash
$ python re_finditer_demo.py
If you want to using regular expression, you must import the re module
Pattern: epression
Pattern: you
group: you
start: 3
end: 6
span: (3, 6)
group: you
start: 41
end: 44
span: (41, 44)
Pattern: ^If.*to
group: If you want to
start: 0
end: 14
span: (0, 14)
Pattern: module$
group: module
start: 64
end: 70
span: (64, 70)
sub()、subn() の使用例
string を正規表現 pattern にマッチするすべての重複しないマッチを、repl で置換した結果を返します。パターンが見つからなかった場合、 string がそのまま返されます。
repl には文字列または関数を与えることができます。
count には置換する最大数を与えます。省略されるか 0 で出現した全てが置換されます。
matchObj = sub(pattern, repl, string, count, flag)
pattern: 正規表現のパターン、もしくはパターンオブジェクトを指定
repl: 置換文字列、もしくは関数オブジェクトID
string: 検索対象の文字列
count: 置換する最大数
flag: REフラグ (詳しくは後述します)
matchObj: マッチオブジェクトが yield で返されます。
(new_string、 number_of_subs_made) = subn(pattern, repl, string, count, flag)
pattern: 正規表現のパターン、もしくはパターンオブジェクトを指定
repl: 置換文字列、もしくは関数オブジェクトID
string: 検索対象の文字列
count: 置換する最大数
flag: REフラグ (詳しくは後述します)
new_string: 置換後の文字列
number_of_subs_made:
code: re_sub_demo.py
import re
demos=[
('Delete Pattern abc', 'abc', ''),
('Replace pattern abc->def', 'abc', 'def'),
('Eliminate duplicate whispaces', r'\s+', ' '),
('Replace a string with a part of itself', 'abc(def)ghi', r'\1'),
]
text = 'abcdef abcdefghi ABCDEF abcdefdeghi'
print(f'TEST string: "{text}\n')
for testcase, pattern, repl in demos:
result = re.sub( pattern, repl, text)
print(f'{testcase}: "{result}"')
newtext, num = re.subn( pattern, repl, text)
print(f'newtex: {newtext}, number of subs: {num}')
print()
code: bash
$ python re_sub_simple.py
TEST string: "abcdef abcdefghi ABCDEF abcdefdeghi
Delete Pattern abc: "def defghi ABCDEF defdeghi"
newtex: def defghi ABCDEF defdeghi, number of subs: 3
Replace pattern abc->def: "defdef defdefghi ABCDEF defdefdeghi"
newtex: defdef defdefghi ABCDEF defdefdeghi, number of subs: 3
Eliminate duplicate whispaces: "abcdef abcdefghi ABCDEF abcdefdeghi"
newtex: abcdef abcdefghi ABCDEF abcdefdeghi, number of subs: 3
Replace a string with a part of itself: "abcdef def ABCDEF abcdefdeghi"
newtex: abcdef def ABCDEF abcdefdeghi, number of subs: 1
split() の使用例
string を、出現した正規表現 pattern で分割した、文字列のリストを返します。
pattern 中でグループ化の丸括弧が使われていれば、パターン中の全てのグループのテキストも結果のリストの一部として返されます。maxsplit が 0 でなければ、分割は最大 maxsplit 回起こり、残りの文字列はリストの最終要素として返されます。
listObj = split(pattern, string, maxsplit=0, flags=0)
pattern: 正規表現のパターンを指定
string: 検索対象の文字列
maxsplit: 分割最大数を指定
flag: REフラグ (詳しくは後述します)
listObj: 分割された文字列がリストとして返されます
code: re_split_demo.py
import re
text='If you want to using regular expression, you must import the re module'
patterns=[
'epression',
'you',
'^If.*to',
'module$',
]
print(text)
for ptn in patterns:
x = re.split(ptn, text)
print(f'Pattern: {ptn} {x}')
これを実行すると次のようになります。
code: bash
$ python re_split_demo.py
If you want to using regular expression, you must import the re module
Pattern: ^If.*to ' using regular expression, you must import the re module'
分割する正規表現 pattern は、返されるリストには含まれていないことに留意してください。
compile() の使用例
正規表現 pattern を 正規表現オブジェクト にコンパイルして返します。
何度も呼び出されるようなマッチング処理のときに速度向上が期待できます。
モジュールレベルのマッチング関数(re.match(), re.sarch() など) に渡されたパターンは、直近のものがコンパイルしてキャッシュされています。
このため、僅かな回数しか実行されないマッチング処理のためにコンパイルする必要はありません。
reObj = compile(pattern, flags=0)
pattern: 正規表現のパターンを指定
flag: REフラグ (詳しくは後述します)
reObj: 正規表現オブジェクトが返されます。
code: re_compile_demo.py
import re
text='If you want to using regular expression, you must import the re module'
ptn = re.compile('you')
x = ptn.match(text)
print('match')
if x != None:
print(f'group: {x.group()}')
print(f'start: {x.start()}')
print(f' end: {x.end()}')
print(f' span: {x.span()}')
print('search')
x = ptn.search(text)
if x != None:
print(f'group: {x.group()}')
print(f'start: {x.start()}')
print(f' end: {x.end()}')
print(f' span: {x.span()}')
print('findall')
print(ptn.findall(text))
code: bash
$ python re_compile_demo.py
match
search
group: you
start: 3
end: 6
span: (3, 6)
findall
正規表現オブジェクト
グループ化 (...)
丸括弧で囲まれた正規表現のパターンは、マッチしたグループの開始と終了を表します。
グループ化された文字列、マッチが実行された後で抽出したり、その文字列中で以降 \number 特殊シーケンスでマッチさせたりできます。
丸括弧( '(' や ')' ) にマッチさせるためには、バックスラッシュ記号(\`) で、エスケープ(\( や \))するか、文字クラス中に囲みます([(]や [)])
丸括弧続けてクエッション記号(?)があるときは、その次の文字によって意味や動作が変わってきます。
(?# ...) の場合
(?# ...) と記述されているときは括弧のなかはコメントになり、意味を持ちません。
(?<name>...) の場合
(?<mame>...) と記述されているときは、パターン... にマッチしたものにグループ名name が割り当てられマッチオブジェクトで参照することができるようになります。
(?=...) の場合
パターン ... が次に文字列にマッチすればマッチします。
先読みアサーション (lookahead assertion) といいます。
通常の場合と違って文字列をまったく消費しません。
例えば、Isaac (?=Asimov) は 'Isaac ' に、その後に 'Asimov' が続く場合にのみ、マッチします。
(?!...) の場合
パターン ... が次に続く文字列にマッチしなければマッチします。
否定先読みアサーション (negative lookahead assertion) といいます。
通常の場合と違って文字列をまったく消費しません。
例えば、Isaac (?!Asimov) は 'Isaac ' に、その後に 'Asimov' が続か ない 場合にのみ、マッチします。
(?<=...) の場合
その文字列における現在位置の前に、現在位置で終わるパターン ... にマッチすれば、マッチします。
肯定後読みアサーション(positive lookbehind assertion) といいます。
(?<=abc)def は、3 文字abcをバックアップし、含まれているパターンがマッチするか検査するので 'abcdef' にマッチを見つけます。
含まれるパターンは、固定長の文字列にのみマッチしなければなりません。
つまり、 abc や a|b とパターンを記述することはできますが、a* や a{3,4} のように可変長のパターンは記述することはできません。
肯定後読みアサーションで始まるパターンは、検索される文字列の先頭とは決してマッチしないことに注意して下さい。
re.match() ではなく re.search() を使う方がよいでしょう。
REフラグ
reモジュールの各関数に与えることができる REフラグには次のものを、
ビット単位 OR ( | 演算子) で組み合わせて与えることができます。
table: REフラグ
re.A / re.ASCII ASCII限定マッチを行う、非ASCIIマッチは無効になる
re.I / re.IGNORECASE 大文字小文字は区別しない
reL / re.LOCAL ロケールに依存したマッチを行う。推奨されていない
re.M / re.MULTILINE 対象文字列を複数行として扱う
re.S / re.DOTALL メタ文字ドット(.)をあらゆる文字にマッチさせる
このフラグがないとメタ文字ドットは改行にはマッチしない
re.X / re.VERBOSE パターンを視覚的に分割して読みやすく書けるようにする
code: re_grouping_demo.py
import re
text = "Coronavirus concerns lead to hoarding, panic buying to stock 'panic pantries,'"
searchObj = re.search( r'(?P<virus>corona^ *) .* to (.*?) .*', text, re.M|re.I)
if searchObj:
print(f'searchObj.group() : {searchObj.group()}')
print(f'searchObj.group(virus) : {searchObj.group("virus")}')
print(f'searchObj.group(1) : {searchObj.group(1)}')
print(f'searchObj.group(2) : {searchObj.group(2)}')
else:
print('Nothing found!!')
code: bash
$ python re_grouping_demo.py
searchObj.group() : Coronavirus concerns lead to hoarding, panic buying to stock 'panic pantries,'
searchObj.group(virus) : Coronavirus
searchObj.group(1) : Coronavirus
searchObj.group(2) : stock
よく利用される正規表現
Emailアドレス
^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
ドメイン名
^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$
インタネットURL
^(http|https)://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
固定電話
^0\d-\d{4}-\d{4}$
携帯電話
^(070|080|090)-\d{4}-\d{4}$
IP電話
^050-\d{4}-\d{4}$
日本のフリーダイヤル
^0120-\d{3}-\d{3}$
日付 (YYYY-MM-DD)
^\d{4}-\d{1,2}-\d{1,2}$
郵便番号
^\d{3}-\d{4}$
XML
^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
IPアドレス
((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
N桁数字
^\d{n}$
N桁以下数字
^\d{n,}$
参考:
正規表現の動作を確認できるサービス