【 Python + Regular Expressions 】複数のマッチパターンを look ahead (先読み) シンタックスを利用して 1 つのマッチパターンにまとめて処理しよう! re.match() の呼び出しも 1 回で済んじゃいます! 投稿一覧へ戻る
Published 2020年9月11日20:20 by mootaro23
SUPPORT UKRAINE
- Your indifference to the act of cruelty can thrive rogue nations like Russia -
時には、テキストが複数の条件に合致しているか、を調べる必要に迫られることもあります。
ユーザーが設定しようとしているパスワードが十分に「安全」かを確認したいような場合もそうですね。
ここでは「十分に安全」なパスワード ( strong passowrd ) の条件を以下のように定めるとします。。
さて、もっとも着実な確認方法は、それぞれの条件に当てはまるパターンを用意し、条件の数だけ re.match() を呼び出して別々にマッチを確認、最終的に全てパターンがマッチすれば strong password であると判断する、というものでしょう。
しかし、look-ahead シンタックスを利用することで、より効率的に複数のパターンマッチを確認することができちゃいます!
re.match() の呼び出しも 1 回で済んじゃいますよ。
さて、その方法ですが、まずは各条件を満たすパターンを用意します。この場合は 5 パターンですね。
最初のパターンを除く他の全てのパターンでは look ahead シンタックスを採用しています。
これらのパターンを繋げて「長い」1 つのパターンを作成しましょう。
唯一 look ahead シンタックスを利用していない 1 つ目のパターンは最後に連結しますョ!
さて、ここでもう一度、look-ahead シンタックスの特徴を思い出してください。
lookahead シンタックスにマッチしたサブ文字列は「消費」されない、のでした。
つまり 「先読み」されたパターン部分は、続くマッチ検索において再び「検索文字列の一部」として検索対象になる、んですね。
よって、文字列の先頭から 1 文字ずつ全てのパターンのマッチが調べられながら進んでいくことになります。
そして、pat1 から pat5 すべてのパターンがマッチした段階で、これらのパターンを併せた pat_all もマッチしたことになるんですね。
さて最後にちょっとだけ補足です。
pat1 と pat5 では「文字集合、および、文字範囲」を表す [ ] シンタックスを使っています。
この [ ] シンタックスの中で利用される '-' 文字は、文字範囲を表すための特殊な意味を持ちます。
例えば、[a-z] であれば、小文字のアルファベット 'a' から 'z' のうちの 1 文字にマッチする、という意味ですよね。
ただし、[ ] の中の先頭、もしくは、最後に置かれている '-' 文字はその特殊な意味を失って、リテラル文字として扱われます。
ですから、pat1 と pat5 における [ ] 内の '-' 文字は、特殊文字ではなくて普通の「マイナスサイン」としてのリテラル文字を意味しています。
ユーザーが設定しようとしているパスワードが十分に「安全」かを確認したいような場合もそうですね。
ここでは「十分に安全」なパスワード ( strong passowrd ) の条件を以下のように定めるとします。。
1: 8 文字以上で、アルファベット、数字、句読文字 ( punctuation character ) のいずれもが使用されていること。
2: 少なくとも 1 文字以上のアルファベットの大文字を含むこと
3: 少なくとも 1 文字以上のアルファベットの小文字を含むこと
4: 少なくとも 1 文字以上の数字を含むこと
5: 少なくとも 1 文字以上の句読文字を含むこと
さて、もっとも着実な確認方法は、それぞれの条件に当てはまるパターンを用意し、条件の数だけ re.match() を呼び出して別々にマッチを確認、最終的に全てパターンがマッチすれば strong password であると判断する、というものでしょう。
しかし、look-ahead シンタックスを利用することで、より効率的に複数のパターンマッチを確認することができちゃいます!
re.match() の呼び出しも 1 回で済んじゃいますよ。
さて、その方法ですが、まずは各条件を満たすパターンを用意します。この場合は 5 パターンですね。
pat1 = r'(\w|[!@#$%^&*+-]){8,}$'
pat2 = r'(?=.*[A-Z])'
pat3 = r'(?=.*[a-z])'
pat4 = r'(?=.*\d)'
pat5 = r'(?=.*[!@#$%^&*+-])'
pat2 = r'(?=.*[A-Z])'
pat3 = r'(?=.*[a-z])'
pat4 = r'(?=.*\d)'
pat5 = r'(?=.*[!@#$%^&*+-])'
最初のパターンを除く他の全てのパターンでは look ahead シンタックスを採用しています。
これらのパターンを繋げて「長い」1 つのパターンを作成しましょう。
唯一 look ahead シンタックスを利用していない 1 つ目のパターンは最後に連結しますョ!
pat_all = pat2 + pat3 + pat4 + pat5 + pat1
さて、ここでもう一度、look-ahead シンタックスの特徴を思い出してください。
lookahead シンタックスにマッチしたサブ文字列は「消費」されない、のでした。
つまり 「先読み」されたパターン部分は、続くマッチ検索において再び「検索文字列の一部」として検索対象になる、んですね。
よって、文字列の先頭から 1 文字ずつ全てのパターンのマッチが調べられながら進んでいくことになります。
そして、pat1 から pat5 すべてのパターンがマッチした段階で、これらのパターンを併せた pat_all もマッチしたことになるんですね。
import re
pattern = re.compile(pat_all)
passwd = '12Nana345Saki6^789'
if pattern.match(passwd):
print('有効なパスワードです')
else:
print('危険!危険!')
# 有効なパスワードです
passwd = '12nana345saki6^789' # 大文字を含んでいません
if pattern.match(passwd):
print('有効なパスワードです')
else:
print('危険!危険!')
# 危険!危険!
passwd = '2Na5sa^' # 7 文字です
if pattern.match(passwd):
print('有効なパスワードです')
else:
print('危険!危険!')
# 危険!危険!
pattern = re.compile(pat_all)
passwd = '12Nana345Saki6^789'
if pattern.match(passwd):
print('有効なパスワードです')
else:
print('危険!危険!')
# 有効なパスワードです
passwd = '12nana345saki6^789' # 大文字を含んでいません
if pattern.match(passwd):
print('有効なパスワードです')
else:
print('危険!危険!')
# 危険!危険!
passwd = '2Na5sa^' # 7 文字です
if pattern.match(passwd):
print('有効なパスワードです')
else:
print('危険!危険!')
# 危険!危険!
さて最後にちょっとだけ補足です。
pat1 と pat5 では「文字集合、および、文字範囲」を表す [ ] シンタックスを使っています。
この [ ] シンタックスの中で利用される '-' 文字は、文字範囲を表すための特殊な意味を持ちます。
例えば、[a-z] であれば、小文字のアルファベット 'a' から 'z' のうちの 1 文字にマッチする、という意味ですよね。
ただし、[ ] の中の先頭、もしくは、最後に置かれている '-' 文字はその特殊な意味を失って、リテラル文字として扱われます。
ですから、pat1 と pat5 における [ ] 内の '-' 文字は、特殊文字ではなくて普通の「マイナスサイン」としてのリテラル文字を意味しています。
この記事に興味のある方は次の記事にも関心を持っているようです...
- People who read this article may also be interested in following articles ... -