1. ホーム
  2. Web プログラミング
  3. 正規表現

正規表現の量詞の使い方と欲張りな説明

2022-01-18 09:05:19

0. 前面に書く

前の記事 は、レギュラーの基本的なメタキャラクタを学びましたが、これはかなり忘れていると思いますので、上のリンクをクリックして、リフレッシュしてください。

今日は、regularの量詞の3つのマッチングモード、greedyモード、non-greedyモード、exclusiveモードを学びます。これらのモードは、regularの量詞のマッチング動作を変更します、それは毎回よりマッチするように貪欲ですか、それを見て貪欲ではない、我々はこれらを理解していない場合、我々は正規は間違っているとさえ深刻なオンラインパフォーマンス問題を引き起こす可能性があります書かれています。

1. 量詞

この記事で扱う内容は、量詞とより密接に関連するもので、まず、「量詞」の復習から始めます。

また {m,n} とする。 を表現するために * + ? 3つのメタキャラクタ。

<テーブル メタキャラクタ 同義語表現 例 * {0,} ab*
に一致させることができます。
a または abb + {1,} ab+
に一致させることができます。
ab または abb
にはマッチしませんが ? {0,1} ab?
aまたはabにマッチすることができる
にはマッチするが、abb にはマッチしない

2. 貪欲モード前日譚

レギュラーでは、回数を示す量詞はデフォルトで greedy になっています。greedyモードでは、対象の文字列に対して可能な限りの長さをマッチングさせるため、レギュラーの a+ a * 文字列にマッチするように aaabb テストしてみてください。

2.1 a+を使ったマッチング

1つの結果だけがaaaにマッチしていることがわかります。

対応するPythonのコードは以下の通りです。

import re

print(re.findall(r'a+', 'aaabb'))

Output: ['aaa']


2.2 a*を用いたマッチング

4つの結果がマッチし、そのうち3つは空の文字列であることがわかります。

対応するPythonのコードは以下の通りです。

import re

print(re.findall(r'a*', 'aaabb'))

Output: ['aaa', '', '', '']


なぜ空の文字列にマッチするのですか?アスタリスク(*)は0~多数のマッチを意味し、0マッチは空文字列だからです。では、なぜaaaとaaaの間の空文字列はマッチしないのでしょうか?

ここで、本日のテーマである「欲張りパターン vs. 非欲張りパターン」の話になるのですが、文字通り、欲張りパターンはできるだけ多くの回数をマッチングし、非欲張りパターンはできるだけ少ない回数をマッチングする、という単純なものです。

3. 貪欲なパターン

上記のレギュラーを分解してみましょう a * のマッチング処理を行います。

<テーブル 文字列 a a a b b 空の文字列 添え字 0 1 2 3 4 5

<テーブル 一致 開始 終了 説明 適合するコンテンツ 初回 0 3 最初の文字bに移動して不一致を見つけ、aaaを出力する aaa 2番目 3 3 bbの残りにマッチ、マッチしない場合、空文字列を出力する 空の文字列 3番目 4 4 bの残りにマッチ、マッチしない場合、空文字列を出力する 空の文字列 第四 5 5 残りの空文字列にマッチし、空文字列を出力する 空の文字列

a* 文字列 aaabb にマッチする場合、最初の b にマッチせず、3 つの a にマッチし、それ以降のマッチはそれぞれ空文字列になるまで、できるだけ多くの先行する a にマッチします。

できるだけ最大長でマッチングするのが特徴のGreedyパターンについては、皆さんもよくご存知だと思いますが、その真逆のパターンも一緒に見ていきましょう。

4. 非グリーディパターン

greedyパターンについて話した後、greedyパターンは可能な限り最大長のマッチ、non-greedyパターンは可能な限り最小長のマッチで、量詞の後にクエスチョンマーク(?)を付けるとnon-greedyパターンになり、例えばa*?

対応するPythonのコードは以下の通りです。

import re

// greedy match
print(re.findall(r'a*', 'aaabb'))

Output: ['aaa', '', '', '']

// Non-greedy matching
print(re.findall(r'a*?' , 'aaabb'))

Output: ['', 'a', '', 'a', '', 'a', '', '', '', '']


欲張りパターン、非欲張りパターンについて学んだ後、どのような場面でそれらを使えばいいのか、と聞かれることがあります。

要件は、文字列中のダブルクォートで囲まれた内容をすべて見つけることであり、上記の欲張りマッチングと非欲張りマッチングを使えば、その違いは明らかでしょう?

5. 排他的モード

バックトラックは必要ないが、マッチしない場合は失敗を返せばよいというシナリオもある。そこで、レギュラー・マッチングには排他的という別のパターンがある。これは greedyと非常によく似ているが、マッチング中にバックトラックを行わないので、シナリオによってはこちらの方がよい。

まず、バックトラックとは何かということから、正規表現と対象文字列を見て、3つのマッチング・モードそれぞれでどうなるかを見てみましょう。

5.1 欲張りマッチング処理

<ブロッククオート

正規表現:ab{1,3}c

対象文字列:abbc

マッチングの際、b{1,3}は対象の文字列とマッチするために可能な限り長く進みます。abbとマッチした後、可能な限り長く(3つのb)マッチするために、対象の文字列のcはマッチしません。このとき、フォワードバックトラックが起こり、現在の文字cを吐き出し、マッチするように通常のcを使って、マッチは成功するのです。

import regex

print(regex.findall(r'ab{1,3}c', 'abbc'))

Output: ['abbc']


5.2 非欲求型マッチング処理

正規表現:ab{1,3}?c

対象文字列:abbc

マッチングの際、b{1,3}は対象文字列をできるだけ短くマッチングし、abをマッチングした後、対象文字列の残りのbを直接正規のcとマッチングし、マッチしなかった場合、バックトラックして対象文字列の残りのbを正規b{1,3}とマッチし、正規cは残りcとマッチし、マッチングは成功します。

import regex

print(regex.findall(r'ab{1,3}?c', 'abbc'))

Output: ['abbc']


5.3 排他的論理和処理

量詞に+をつけると排他的パターンになります。

正規表現:ab{1,2}+bc

対象文字列:abbc

マッチングの際、b{1,2}は対象の文字列を可能な限り長くマッチングし、abbにマッチした後、対象の文字列の残りのcを正規のbでマッチングし、マッチしない場合は後戻りせず、マッチングは失敗します。

import regex

print(regex.findall(r'ab{1,2}+bc', 'abbc'))

Output: []


6. 最後に書く

最後に、上で言われたことをまとめると。

これで量詞と欲の正規表現は終わりです。もし質問があれば、私にコメントを残してください、ありがとうございます。

正規表現オンライン検証ツール。 https://regex101.com/

正規表現の量化子と貪欲の使用に関するこの記事がすべてです、より関連する正規表現の量化子と貪欲のコンテンツは、スクリプトハウスの過去の記事を検索するか、次の関連記事を参照してください、あなたは将来的にもっとスクリプトハウスをサポートすることを望みます