正規表現基礎
* 繰り返し
カテゴリー:量指定子
0回でもOK!
それでは、ここから正規表現の中で、文字列パターンの繰り返しを表現する量指定子をみていきます。
この量指定子は、パターンの一部が何度も繰り返される場合に、その威力を発揮します。
例えば、対象文字列が bee だとします。
この場合、マッチさせる最も単純なパターンとして bee と表現出来ます。
見ての通り ee と e を二回分記述しています。
ここで量指定子を用いると、繰り返しを表現できるので一回分の記述で済みます。
今回は、量指定子の中でも * を使います。
* は、直前の正規表現を 0 回以上、できるだけ多く繰り返す事を意味します。
上の例であるならば、 e* のようにすると、e の繰り返しを表せます。
故に、be* のようにパターンを構成できます。
また * は、直前の正規表現を 0 回以上、としています。
したがって、e* ならば e が0回、つまり対象とする文字列が b の場合にもマッチします。
パターンが be* の場合
今回もシンプルな例を試しながら、徐々に慣れていきましょう。
この記事の難度は、基礎 Cクラスです。
(A: やさしい → E: 難しい)
事前知識として、pythonから正規表現を扱う方法が必要になります。
また、正規表現における文字クラスの知識や、グループを表す正規表現である ( ) を理解している事が望ましいです。
(不安な人でも、【Pythonから使う】【基礎1 文字クラス】 【( ) グループを指定】【( ) キャプチャを使う】、で詳しい解説があるので安心です。)
先ずは簡単な例で、繰り返しを表現する量指定子に慣れていきます。
* の動作を確認しましょう。
難度 : | |
事前知識: | Pythonの基礎文法(reモジュールを含む)。正規表現の文字クラスやグループ等。 |
学習効果: | * が直前の正規表現を 0 回以上、できるだけ多く繰り返す事を理解できる。 |
Contens | 目次
Chapter1 | Pythonで実行 |
Chapter1 Pythonで実行
a*
先ず文字列パターンを、アルファベット1文字の a と * を組み合わせて a* とします。
これは a の0回以上の繰り返しを表します。
a はあっても無くてもよいので、対象文字列が map なら、1回出現している a の他にもマッチするはずです。
re_meta24_1.py import re pattern = re.compile("a*") st = "map" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("match:",result.group()) print("位置",result.span())
実行結果
予想通り、a 以外にもマッチしました。
次にパターンを ma*p にします。
m と p の間の a が繰り返すか、あるいは無い場合に一致するはずです。
re_meta24_2.py import re pattern = re.compile("ma*p") st = "mp map maap" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("match:",result.group()) print("位置",result.span())
実行結果
これも想定通りの結果です。
a が存在しない mp や、繰り返している map , maap などと一致しました。
これに対して、対象文字列を mop としてしまうと、a の代わりに o が存在するのでマッチしません。
re_meta24_3.py import re pattern = re.compile("ma*p") st = "mop" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("match:",result.group()) print("位置",result.span())
実行結果
mop ではマッチしない事が確認出来ました。
さて、ここで冒頭で例示した bee について実行してみましょう。
パターンを be* と構成する事で、e のできるだけ多くの繰り返しにマッチします。
よって、対象文字列が beeeeeee のように、e が多く繰り返されていてる場合でもヒットします。
re_meta24_4.py import re pattern = re.compile("be*") st = "bee b beeeeeee" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("match:",result.group()) print("位置",result.span())
実行結果
bee だけでなく、b や beeeeeee にもヒットしています。
(es)*
今度は、グループ化されたものを繰り返しの対象とします。
グループを表す正規表現は ( ) です。
(グループ については【( ) グループを指定】で詳しく説明しています。)
パターンを (es) の繰り返しにする場合、(es)* のように記述します。
re_meta24_5.py import re pattern = re.compile("gen(es)*") st = "gen genes geneses" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("match:",result.group()) print("位置",result.span())
実行結果
それぞれ、es の0回の繰り返しである gen あるいは、1回、2回である genes , geneses に一致しました。
さらに、次の例では (es) の後方参照を試しています。
(後方参照 については【( ) キャプチャを使う】で詳しく説明しています。)
パターンは、シンプルに (es)*\1 にしましょう。
対象文字列は、先程と同様なものに geneseseses 加えた gen genes geneses geneseseses です。
re_meta24_6.py import re pattern = re.compile(r"gen(es)*\1") st = "gen genes geneses geneseseses" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("----- match -----") for i in range(result.lastindex + 1): print('group{num};'.format(num = i),result.group(i)) print("位置",result.span(i))
実行結果
実行結果(続き)
geneses , geneseseses にはマッチしましたが、gen , genes にはマッチしていません。
グループに量指定子を組み合わせてから、後方参照を (es)*\1 のように行っても、あくまで es と、その参照が必要になる事は変わらないようです。
((es)*)
次の例では、(es)* 自体を ( ) で括ってグループ化してみましょう。
パターン以外は、一つ前に実行した meta24_6.py と同様にして、結果の違いに注意しながら試します。
re_meta24_7.py import re pattern = re.compile(r"gen((es)*)\1") st = "gen genes geneses geneseseses" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("----- match -----") for i in range(result.lastindex + 1): print('group{num};'.format(num = i),result.group(i)) print("位置",result.span(i))
実行結果
実行結果(続き)
実行結果(続き2)
meta24_6.py のときとは異なり、対象文字列の全てに一致しました。
各々、0回、1回、2回分に該当する後方参照が行われたようです。
\d*
続けて例を出します。
数字を表す文字クラスである \d と * を組み合わせて、数字の繰り返しを狙います。
(文字クラス については【\d 数字を指定する】で詳しく説明します。)
re_meta24_8.py import re pattern = re.compile("¥\d*") st = "¥ ¥7 ¥77 ¥777" print("↓ 対象文字列\n"+st) result_iter = pattern.finditer(st) for result in result_iter: print("match:",result.group()) print("位置",result.span())
実行結果
実行結果(続き)
0回の分も含めて ¥ 表示の金額を取得出来ました。
以上で * の解説は終わりにします。
平易な例を通じて、* が直前の正規表現を 0 回以上、できるだけ多く繰り返す事を理解できました。
量指定子は * 以外にもあるので、次回は + を学習しましょう。
関連記事
* 繰り返し
正規表現: | 量指定子 |
難度 : | 基礎 |
事前知識: | Pythonの基礎。文字クラスやグループ等。 |
学習効果: | * が直前の正規表現を 0 回以上、できるだけ多く繰り返す事を理解できる。 |
+ 繰り返し
正規表現: | 量指定子 |
難度 : | 基礎 |
事前知識: | Pythonの基礎。文字クラスやグループ等。 |
学習効果: | + が直前の正規表現を 1 回以上、できるだけ多く繰り返す事を理解できる。 |
? 繰り返し
正規表現: | 量指定子 |
難度 : | 基礎 |
事前知識: | Pythonの基礎。文字クラスやグループ等。 |
学習効果: | ? が、直前の正規表現を0回か、1回繰り返したものにマッチさせる事を理解できる。 |
{ } 繰り返し
正規表現: | 量指定子 |
難度 : | 基礎 |
事前知識: | Pythonの基礎。文字クラスやグループ等。 |
学習効果: | { } が、直前の正規表現を指定した回数、ちょうど繰り返したものにマッチさせる事を理解できる。 |
{ , } 繰り返し
正規表現: | 量指定子 |
難度 : | 基礎 |
事前知識: | Pythonの基礎。文字クラスやグループ等。 |
学習効果: | {m,n} が、直前の正規表現を m 回から n 回、できるだけ多くの繰り返しにマッチする事が分かる。 |
*? +? ?? { , }? 繰り返し
正規表現: | 量指定子 |
難度 : | 基礎 |
事前知識: | Pythonの基礎。文字クラスやグループ等。 |
学習効果: | *? +? ?? { , }? が、非貪欲 (non-greedy)のマッチになる事を理解できる。 |
正規表現をPythonから使うには ?
正規表現: | Pythonから使う |
難度 : | 入門 |
事前知識: | Pythonの基礎文法 |
学習効果: | pythonから正規表現を使う一連の流れを掴む |
ハロー ! メタキャラクタ
正規表現: | メタキャラクタの概要 |
難度 : | 入門 |
事前知識: | 不要 |
学習効果: | メタキャラクタの概要を掴む |
正規表現とは?
正規表現: | 概要 |
難度 : | 入門 |
事前知識: | 不要 |
学習効果: | 正規表現の概要を知る |
PR