正規表現

量指定子 { , } _key-visual

範囲を持たせて...

 前回から引き続き、量指定子の { } についての説明です。

{ } が、直前の正規表現を指定した回数、ちょうど繰り返したものにマッチさせる事を前回学習しました。

 今回は、繰り返し回数に範囲を持たせる事をみていきます。

具体的には、{ } の中に , を付けて { , } のようにすると、繰り返し回数に範囲を持たせる事が可能です。

記述例として e{1,3} で、1個から3個の e にマッチします。

 つまり {m,n} は、直前の正規表現を m 回から n 回、できるだけ多く繰り返したものにマッチさせる正規表現という訳です。

それゆえに、文字列パターンを be{1,3} とすると、bebee にはマッチしますが、beeeeeee などにはマッチせずに beee までのマッチになります。

パターンが be{1,3} の場合 パターンが be{1,3} の場合のフローチャート


 また、{m,n} において m を省略して {,n} にすると下限は 0 に指定されます。

例えば、e{,3} なら e の0回から3回までの繰り返しを表します。

先程のパターンに適用して be{,3} にするならば、bbe あるいは bee にマッチし、beeeeeee には beee までマッチします。

パターンが be{,3} の場合 パターンが be{,3} の場合のフローチャート

 さらに {m,n} につき、{m,} のように n を省略すると上限は無限に指定されます。

よって、e{1,}e の1回以上で上限無しの繰り返しを表現しています。

パターンが be{1,} なら、bebee ないしは beeeeeee と一致します。

パターンが be{1,} の場合 パターンが be{1,} の場合のフローチャート

 ところで、前回までに説明した量指定子である * + ? は、{ , } を用いて表現できる事に気付いたでしょうか?

つまり、{0,}* と等しく、 {1,}+ と、そして {0,1}? と同様の表現です。

* + ? を { , } を用いて表現する * + ? を { , } を用いて表現する

 それでは、シンプルな例を使って { , } の動作を確認していきましょう。


この記事の難度は、基礎  Cクラスです。

(A: やさしい   →   E: 難しい)

 事前知識として、pythonから正規表現を扱う方法が必要になります。
また、正規表現における文字クラスの知識や、グループを表す正規表現である ( ) を理解している事が望ましいです。
(不安な人でも、【Pythonから使う】【基礎1 文字クラス】 【( ) グループを指定】【( ) キャプチャを使う】、で詳しい解説があるので安心です。)
 記述方法に注意しながら、繰り返しに範囲を持たせる事を試していきましょう。

難度       :
事前知識: Pythonの基礎文法(reモジュールを含む)。正規表現の文字クラスやグループ等。
学習効果:  {m,n} が、直前の正規表現を m 回から n 回、できるだけ多く繰り返したものにマッチさせる事を理解できる。

Contens  |   目次

Chapter1 Pythonで実行

Chapter1   Pythonで実行

a{1,2}

 先ず文字列パターンを、アルファベット1文字の a{1,2} を組み合わせて a{1,2} とします。

これは a の1回から2回までの繰り返しを表します。

a は1個か2個必要なので、対象文字列が map なら a にマッチするはずです。

    re_meta28_1.py

        import  re

        pattern = re.compile("a{1,2}")
        st = "map"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_1

 予想通り、a にマッチしました。

次にパターンを ma{1,2}p にします。

mp の間に a が1個か2個ある場合にのみ一致するはずです。

    re_meta28_2.py

        import  re

        pattern = re.compile("ma{1,2}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())        
                            


実行結果 量指定子 { , }_2

 a が1個の map 及び、2回繰り返している maap に一致しました。

これに対して、対象文字列を mop としてしまうと、a の代わりに o が存在するのでマッチしません。

    re_meta28_3.py

        import  re

        pattern = re.compile("ma{1,2}p")
        st = "mop"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_3

 mop ではマッチしない事が確認出来ました。

e{1,3} e{,3} e{1,}

 さて、ここで冒頭で例示した bee について実行してみましょう。

先ずパターンを be{1,3} と構成する事で、e の1回から3回の繰り返しにマッチさせます。

対象文字列が beeeeeee のように、e が多く繰り返されていてる場合でも、3回分の繰り返しまでしかヒットしないはずです。

    re_meta28_4.py

        import  re

        pattern = re.compile("be{1,3}")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_4

 想定通り、e の1回、2回分の繰り返しである bebee にマッチし、beeeeeee は、beee までしかヒットしていません。

次にパターンを be{,3} と構成する事で、e の3回以内の繰り返しにマッチさせます。

    re_meta28_5.py

        import  re

        pattern = re.compile("be{,3}")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_5

実行結果(続き) 量指定子 { , }_5_2

 be{1,3} のときとは異なり、e の0回の繰り返しである b にも一致しています。

その他は、be{1,3} と同様です。

 今度は、文字列パターンを be{1,} として、e の1回以上の繰り返しにマッチさせます。

    re_meta28_6.py

        import  re

        pattern = re.compile("be{1,}")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_6

 e の繰り返しの上限が無くなったので beeeeeee にもマッチしました。

(es){1,2}

 今度は、グループ化されたものを繰り返しの対象とします。

グループを表す正規表現は ( ) です。

(グループ については【( ) グループを指定】で詳しく説明しています。)

パターンを (es) の繰り返しにする場合、(es){1,2} のように記述します。

    re_meta28_7.py

        import  re

        pattern = re.compile("gen(es){1,2}")
        st = "gen genes geneses"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_7

 それぞれ、es が1つの場合は genes 、2つのときは、geneses に一致しています。

 さらに、次の例では (es)後方参照を試しています。

(後方参照 については【( ) キャプチャを使う】で詳しく説明しています。)

 パターンは、シンプルに (es){1,2}\1 にしましょう。

対象文字列は、先程と同様なものに geneseseses 加えた gen genes geneses geneseseses です。

    re_meta28_8.py

        import  re

        pattern = re.compile(r"gen(es){1,2}\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))
                            


実行結果 量指定子 { , }_8

実行結果(続き) 量指定子 { , }_8_2

 geneses , genesesesesgeneseses の箇所 にはマッチしました。

グループに量指定子を組み合わせてから、後方参照を (es){1,2}\1 のように行っても、あくまで es と、その参照が必要になる事は変わらないようです。

 もう一つ例として、(es){1,}\1 の場合も試してみましょう。

    re_meta28_9.py

        import  re

        pattern = re.compile(r"gen(es){1,}\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))
                            


実行結果 量指定子 { , }_9

実行結果(続き) 量指定子 { , }_9_2

 この場合は、geneseseses にもマッチしました。

((es){1,2})

 次の例では、(es){1,2} 自体を ( ) で括ってグループ化してみましょう。

パターン以外は、前に実行した meta28_8.py と同様にして、結果の違いに注意しながら試します。

    re_meta28_10.py

        import  re

        pattern = re.compile(r"gen((es){1,2})\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))
                            


実行結果 量指定子 { , }_10

実行結果(続き) 量指定子 { , }_10_2

 geneses , geneseseses につき、各々1回分と2回分の繰り返しに該当する後方参照が行われたようです。

\d{1,2}

 続けて例を出します。

数字を表す文字クラスである \d{ , } を組み合わせて、数字の繰り返しを狙います。

(文字クラス については【\d 数字を指定する】で詳しく説明します。)

    re_meta28_11.py

        import  re

        pattern = re.compile("¥\d{1,2}")
        st = "¥ ¥7 ¥57 ¥287"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_11

 ¥ 表示の金額を左から1桁あるいは、2桁分取得出来ました。

{0,} = *

 最後に、{0,}* と等しく、 {1,}+ と、そして {0,1}? と同様になる事を確認しましょう。

最初は {0,} = * です。

be{0,} を実行します。

    re_meta28_12.py

        import  re

        pattern = re.compile("be{0,}")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_12

実行結果(続き) 量指定子 { , }_12_2

 be* の実行

    re_meta28_13.py

        import  re

        pattern = re.compile("be*")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_13

実行結果(続き) 量指定子 { , }_12_2

 {0,}* は等しい結果になりました。

{1,} = +

 続いて {1,} = + の確認です。

be{1,} の実行です。

    re_meta28_14.py

        import  re

        pattern = re.compile("be{1,}")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_14

 be+ の実行

    re_meta28_15.py

        import  re

        pattern = re.compile("be+")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_15

 {1,}+ が同様である事を確認出来ました。

{0,1} = ?

 次は {0,1} = ? である事を確かめます。

 be{0,1} の実行

    re_meta28_16.py

        import  re

        pattern = re.compile("be{0,1}")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_16

実行結果(続き) 量指定子 { , }_16_2

 be? の実行

    re_meta28_17.py

        import  re

        pattern = re.compile("be?")
        st = "b be bee beeeeeee"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 量指定子 { , }_17

実行結果(続き) 量指定子 { , }_17_2

 {0,1}? も実行結果が等しくなる事を確かめられました。



 以上で { , } の説明は終了にします。

{m,n} は、直前の正規表現を m 回から n 回、できるだけ多く繰り返したものにマッチさせる事を理解できました。

また、* + ?{ , } を用いて表現できる事も分かりました。

 ここまでの学習で量指定子の多くを習得出来ました。

残るはあと少しです。

*+? を追加すると、非貪欲 (non-greedy) あるいは 最小 (minimal) のマッチが行われる事を次回に解説します。

関連記事

基礎5 量指定子 * _key-visual

* 繰り返し

正規表現: 量指定子
難度       : 基礎
事前知識: Pythonの基礎。文字クラスやグループ等。
学習効果: * が直前の正規表現を 0 回以上、できるだけ多く繰り返す事を理解できる。
基礎5 量指定子 + _key-visual

+ 繰り返し

正規表現: 量指定子
難度       : 基礎
事前知識: Pythonの基礎。文字クラスやグループ等。
学習効果: + が直前の正規表現を 1 回以上、できるだけ多く繰り返す事を理解できる。
基礎5 量指定子 ? _key-visual

? 繰り返し

正規表現: 量指定子
難度       : 基礎
事前知識: Pythonの基礎。文字クラスやグループ等。
学習効果: ? が、直前の正規表現を0回か、1回繰り返したものにマッチさせる事を理解できる。
基礎5 量指定子 { } _key-visual

{ } 繰り返し

正規表現: 量指定子
難度       : 基礎
事前知識: Pythonの基礎。文字クラスやグループ等。
学習効果: { } が、直前の正規表現を指定した回数、ちょうど繰り返したものにマッチさせる事を理解できる。
基礎5 量指定子 { } _key-visual

{ , } 繰り返し

正規表現: 量指定子
難度       : 基礎
事前知識: Pythonの基礎。文字クラスやグループ等。
学習効果: {m,n} が、直前の正規表現を m 回から n 回、できるだけ多くの繰り返しにマッチする事が分かる。
基礎5 量指定子 { } _key-visual

*?   +?   ??   { , }?   繰り返し

正規表現: 量指定子
難度       : 基礎
事前知識: Pythonの基礎。文字クラスやグループ等。
学習効果: *?   +?   ??   { , }?   が、非貪欲 (non-greedy)のマッチになる事を理解できる。
Pythonで正規表現を使う1

正規表現をPythonから使うには ?

正規表現: Pythonから使う
難度       : 入門
事前知識: Pythonの基礎文法
学習効果: pythonから正規表現を使う一連の流れを掴む
メタキャラクタに馴染む_key-visual

ハロー ! メタキャラクタ

正規表現: メタキャラクタの概要
難度       : 入門
事前知識: 不要
学習効果: メタキャラクタの概要を掴む
正規表現の概要_key-visual

正規表現とは?

正規表現: 概要
難度       : 入門
事前知識: 不要
学習効果: 正規表現の概要を知る
正規表現の概要

PR