正規表現

アサーション② (?<! ) 否定後読み _key-visual

指定以外の文字と1つ後の間で ...

 前々回で後読みを説明しました。

これらは、指定した文字の後にマッチする正規表現でした。

 今回紹介する否定後読みは、それとは逆の性質をもち、指定したパターン以外の後でマッチを発生させます。

表記方法は、(?<! ) として、 ! の隣に任意のパターンを設定します。

例えば、(?<!€) ならば 以外の文字と、その後にある文字の間にマッチが生じます。

(?<!€) の場合 対象文字列が¥5 €5 の場合のフローチャート\w


 否定先読みとの違いを示す為に、もう一つ例を挙げます。

指定する文字を a とするならば、否定先読みは (?!a),否定後読ならば、(?<!a) となります。

このパターンに対して、対象文字列が map の場合、下の図のように各々マッチします。

対象文字列が map の場合 対象文字列が¥5 €5 の場合のフローチャート_2\w


 この他に注意点として、否定後読みに指定する任意のパターンに、量指定子は利用出来ない点があります。

(量指定子 については【基礎5 量指定子】で詳しく説明します。)

否定先読みのときと同様に、シンプル、且つ最小構成な例を使い解説します。


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

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

 事前知識として、pythonから正規表現を扱う方法が必要になります。
また、正規表現における文字クラスの知識や、選択を表す正規表現である | OR(または) を理解している事が望ましいです。
(不安な人でも、【Pythonから使う】【基礎1 文字クラス】 【| OR(または)】、で詳しい解説があるので安心です。)
 シンプルな例を一個ずつ確認する事で、否定後読みに慣れていきます。
また、後読みとの違いを意識しましょう。

難度       :
事前知識: Pythonの基礎文法(reモジュールを含む)。正規表現の文字クラスや選択等。
学習効果:  否定後読みの性質である、任意に設定したパターン以外の後でマッチする、という事を理解出来る。

Contens  |   目次

Chapter1 Pythonで実行

Chapter1   Pythonで実行

(?<!a)

 先ず上記した例ですが、文字列パターンが (?<!a) の場合を考えます。

これは、a 以外 とその後にある文字の間にマッチします。

対象文字列が map なら、先頭と m や、m とその後にある a あるいは、p と 末尾の間です。

    re_meta23_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())        
                            


実行結果 アサーション② (?<! )否定後読み_1

 予想通りの位置にマッチしました。

a を先頭や末尾にも並べて、 aaa のようにして実行してみます。

先頭と a の間のみヒットするはずです。

    re_meta23_2.py

        import  re

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


実行結果 アサーション② (?<! )否定後読み_2

 これも想定通りの結果です。

次に空白を挟んで、a a を対象文字列とします。

    re_meta23_3.py

        import  re

        # a と a の間は半角スペース
        pattern = re.compile("(?<!a)")
        st = "a a"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_3

 スペースが存在しても、a 以外と、その後にある文字の間にマッチするという性質は変わりません。

もう一つ例を挙げます。

文字列パターンが (?<!s) であり、対象文字列を seasons とする場合です。

    re_meta23_4.py

        import  re

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


実行結果 アサーション② (?<! )否定後読み_4

 それぞれ s 以外の文字の後にマッチしています。

今度は、(?<! ) だけでなく単語と組み合わせて使ってみます。

(?<!t)s

 対象文字列は、registrants とします。

但し、文字列パターンを (?<!t)s とする事で、t 以外の文字の後にある s という文字にマッチします。

    re_meta23_5.py

        import  re

        pattern = re.compile("(?<!t)s")
        st = "registrants"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_5

 狙い通りの s という文字に一致しました。

次の例では、通貨記号が 以外である数字を取得します。

    re_meta23_6.py

        import  re

        pattern = re.compile("(?<!€)\d")
        st = "¥5 $9 £8 €7"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_6

 通貨表示が 以外の金額を取得出来ました。

メタキャラクタと組み合わせて、 の他に の値も対象外にします。

(?= | )

 選択(OR)を表す正規表現は | です。

( | については【| OR(または)】で詳しく説明しています。)

(?<!€|£)\d のようにすると、 でない金額にマッチします。

    re_meta23_7.py

        import  re

        pattern = re.compile("(?<!€|£)\d")
        st = "¥5 $9 £8 €7"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_7

 ¥5と $9のみ得られました。

(?<! | ) のように、否定後読みと | を併用する場合の注意点として、選択(OR)の対象になる文字数が異なるとエラーが発生する事です。

例えば (?<!令和|R) のような場合です。

    re_meta23_8.py

        import  re

        pattern = re.compile("(?<!令和|R)")
        st = "8年 令和2年 R3年 平成5年"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_8

 look-behind requires fixed-width pattern のエラーが発生しました。

これを回避するには、(?<!令和)(?<!R) のように変更します。

    re_meta23_9.py

        import  re

        pattern = re.compile("(?<!令和)(?<!R)\d")
        st = "8年 令和2年 R3年 平成5年"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_9

 無事にエラーの発生を避けられました。

似たような場合として、(?<!^|R) のような文字列パターンもエラーが生じます。

    re_meta23_10.py

        import  re

        pattern = re.compile("(?<!^|R)")
        st = "8年 令和2年 R3年 平成5年"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_10

 このエラーの回避も、meta23_9.py と同様です。

    re_meta23_11.py

        import  re

        pattern = re.compile("(?<!^)(?<!R)\d")
        st = "8年 令和2年 R3年 平成5年"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_11

 問題無く処理できました。

また、後読みに指定する任意のパターンに、量指定子は利用出来ません。

(量指定子 については【基礎5 量指定子】で詳しく説明します。)

それ故に、(?<!\w*) とした場合にはエラーが発生します。

    re_meta23_12.py

        import  re

        pattern = re.compile("(?<!\w*)\d")
        st = "28年 令和2年 R3年 平成15年"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_12

 look-behind requires fixed-width pattern のエラーです。

さて、この他にも ^ を使ってみます。

^ は、文字列の先頭を意味します。

(^ については【^ 行の先頭】で詳しく説明しています。)

 先頭以外の金額を抽出するには、以下のようにします。

    re_meta23_13.py

        import  re

        pattern = re.compile("(?<!^¥)\d")
        st = "¥2 ¥9 ¥0"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_13

 ¥2 ではなく、¥9¥0 を対象にできました。

続けて例を出します。

. 以外の文字の後にある数字を狙います。

. はそのまま使うと任意の1文字になってしまうので、\ (バックスラッシュ)と併用します。

(\ については【\ エスケープ文字の働き】で詳しく説明しています。)

    re_meta23_14.py

        import  re

        pattern = re.compile("(?<!\.)\d")
        st = "7.8 2.6 a.m"
        print("↓ 対象文字列\n"+st)
        result_iter = pattern.finditer(st)
        for  result   in  result_iter:
            print("match:",result.group())
            print("位置",result.span())        
                            


実行結果 アサーション② (?<! )否定後読み_14

 . 以外の後にある数字のみを取得出来ました。



 否定後読みの説明はここで終了です。

多くの例をみてきたので、否定後読みの性質である、任意に設定したパターン以外の後にマッチする、という事を理解出来ました。

これにより、取得したい部分が、ある文字列の後でない場合に役立つでしょう。


 以上で4回にわたって解説してきた、先読み(後読み)関連の処理については終了です。

指定した文字(指定した文字以外)の、前後の位置に対してマッチさせる一連の操作を習得出来ました。


 先読み(後読み)関連は、正規表現のヤマです。

これを乗り越えられるかどうかは、正規表現の習熟のカギになります。

しかし、ここで多くの人がつまずきます。

容易に理解出来たという人は、上位何割かに入ります。

高い知能があると言えます。

関連記事

基礎4 アサーション② /b _key-visual

\b 単語の境界

正規表現: アサーション②
難度       : 基礎
事前知識: Python基礎(reモジュール)。文字クラス等。
学習効果: 単語の境界とされる位置を的確に認識できるようになる。
基礎4 アサーション② /B _key-visual

\B 単語の境界でない

正規表現: アサーション②
難度       : 基礎
事前知識: Python基礎(reモジュール)。文字クラス等。
学習効果: 単語の境界でない位置を的確に認識できるようになる。
基礎4 アサーション② (?= ) _key-visual

(?= ) 先読み

正規表現: アサーション②
難度       : 基礎
事前知識: Python基礎(reモジュール)。文字クラスや選択等。
学習効果: 先読みの性質である、任意に設定したパターンの前にマッチする、という事を理解出来る。
基礎4 アサーション② (?<= ) _key-visual

(?<= ) 後読み

正規表現: アサーション②
難度       : 基礎
事前知識: Python基礎(reモジュール)。文字クラスや選択等。
学習効果: 後読みの性質である、任意に設定したパターンの後にマッチする、という事を理解出来る。
基礎4 アサーション② (?! ) _key-visual

(?! ) 否定先読み

正規表現: アサーション②
難度       : 基礎
事前知識: Python基礎(reモジュール)。文字クラスや選択等。
学習効果: 否定先読みの性質である、任意に設定したパターン以外の前でマッチする、という事を理解出来る。
基礎4 アサーション② (?<! ) _key-visual

(?<! ) 否定後読み

正規表現: アサーション②
難度       : 基礎
事前知識: Python基礎(reモジュール)。文字クラスや選択等。
学習効果: 否定後読みの性質である、任意に設定したパターン以外の後でマッチする、という事を理解出来る。
Pythonで正規表現を使う1

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

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

ハロー ! メタキャラクタ

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

正規表現とは?

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

PR