正規表現基礎
(?<! ) 否定後読み
カテゴリー:アサーション②
指定以外の文字と1つ後の間で ...
前々回で後読みを説明しました。
これらは、指定した文字の後にマッチする正規表現でした。
今回紹介する否定後読みは、それとは逆の性質をもち、指定したパターン以外の後でマッチを発生させます。
表記方法は、(?<! ) として、 ! の隣に任意のパターンを設定します。
例えば、(?<!€) ならば € 以外の文字と、その後にある文字の間にマッチが生じます。
(?<!€) の場合
否定先読みとの違いを示す為に、もう一つ例を挙げます。
指定する文字を a とするならば、否定先読みは (?!a),否定後読ならば、(?<!a) となります。
このパターンに対して、対象文字列が map の場合、下の図のように各々マッチします。
対象文字列が map の場合
この他に注意点として、否定後読みに指定する任意のパターンに、量指定子は利用出来ない点があります。
(量指定子 については【基礎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())
実行結果
予想通りの位置にマッチしました。
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())
実行結果
これも想定通りの結果です。
次に空白を挟んで、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())
実行結果
スペースが存在しても、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())
実行結果
それぞれ 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())
実行結果
狙い通りの 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())
実行結果
通貨表示が € 以外の金額を取得出来ました。
メタキャラクタと組み合わせて、€ の他に £ の値も対象外にします。
(?= | )
選択(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())
実行結果
¥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())
実行結果
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())
実行結果
無事にエラーの発生を避けられました。
似たような場合として、(?<!^|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())
実行結果
このエラーの回避も、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())
実行結果
問題無く処理できました。
また、後読みに指定する任意のパターンに、量指定子は利用出来ません。
(量指定子 については【基礎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())
実行結果
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())
実行結果
¥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())
実行結果
. 以外の後にある数字のみを取得出来ました。
否定後読みの説明はここで終了です。
多くの例をみてきたので、否定後読みの性質である、任意に設定したパターン以外の後にマッチする、という事を理解出来ました。
これにより、取得したい部分が、ある文字列の後でない場合に役立つでしょう。
以上で4回にわたって解説してきた、先読み(後読み)関連の処理については終了です。
指定した文字(指定した文字以外)の、前後の位置に対してマッチさせる一連の操作を習得出来ました。
先読み(後読み)関連は、正規表現のヤマです。
これを乗り越えられるかどうかは、正規表現の習熟のカギになります。
しかし、ここで多くの人がつまずきます。
容易に理解出来たという人は、上位何割かに入ります。
高い知能があると言えます。
関連記事
\b 単語の境界
正規表現: | アサーション② |
難度 : | 基礎 |
事前知識: | Python基礎(reモジュール)。文字クラス等。 |
学習効果: | 単語の境界とされる位置を的確に認識できるようになる。 |
\B 単語の境界でない
正規表現: | アサーション② |
難度 : | 基礎 |
事前知識: | Python基礎(reモジュール)。文字クラス等。 |
学習効果: | 単語の境界でない位置を的確に認識できるようになる。 |
(?= ) 先読み
正規表現: | アサーション② |
難度 : | 基礎 |
事前知識: | Python基礎(reモジュール)。文字クラスや選択等。 |
学習効果: | 先読みの性質である、任意に設定したパターンの前にマッチする、という事を理解出来る。 |
(?<= ) 後読み
正規表現: | アサーション② |
難度 : | 基礎 |
事前知識: | Python基礎(reモジュール)。文字クラスや選択等。 |
学習効果: | 後読みの性質である、任意に設定したパターンの後にマッチする、という事を理解出来る。 |
(?! ) 否定先読み
正規表現: | アサーション② |
難度 : | 基礎 |
事前知識: | Python基礎(reモジュール)。文字クラスや選択等。 |
学習効果: | 否定先読みの性質である、任意に設定したパターン以外の前でマッチする、という事を理解出来る。 |
(?<! ) 否定後読み
正規表現: | アサーション② |
難度 : | 基礎 |
事前知識: | Python基礎(reモジュール)。文字クラスや選択等。 |
学習効果: | 否定後読みの性質である、任意に設定したパターン以外の後でマッチする、という事を理解出来る。 |
正規表現をPythonから使うには ?
正規表現: | Pythonから使う |
難度 : | 入門 |
事前知識: | Pythonの基礎文法 |
学習効果: | pythonから正規表現を使う一連の流れを掴む |
ハロー ! メタキャラクタ
正規表現: | メタキャラクタの概要 |
難度 : | 入門 |
事前知識: | 不要 |
学習効果: | メタキャラクタの概要を掴む |
正規表現とは?
正規表現: | 概要 |
難度 : | 入門 |
事前知識: | 不要 |
学習効果: | 正規表現の概要を知る |
PR