干し草の山で針を探していると想像してみてください。今度は、特定の形の針をすべて瞬時に見つける磁石を持っていると想像してみてください。これが正規表現の仕組みです。正規表現は、テキストパターンを検索および処理するための強力なツールであり、開発者の何百時間ものルーチン作業を節約します。
正規表現とは
正規表現(regexまたはregexp)は、テキストパターンを記述するための形式言語です。コンパクトな構文を使用して、複雑な規則に従ってテキストを検索、抽出、置換することができます。メールアドレスを検証するために何十行ものコードを書く代わりに、1つの正規表現を使用できます。
正規表現は 1950 年代に数学者のスティーブン・クレイニーによって登場しましたが、Unix システムとテキストエディターの開発により真の人気を博しました。今日では、ほぼすべてのプログラミング言語でサポートされています。
基本構文
まずはシンプルなものから始めましょう。最も基本的な正規表現は通常の文字列です。たとえば、パターン cat は、テキスト内の「cat」という単語のすべてのインスタンスを検索します。しかし、正規表現の真の力は、特殊文字によって明らかになります。
メタシンボルは正規表現の基礎です。
.は、改行以外の任意の文字です。パターン c.t は、「cat」、「cut」、「c9t」などを見つけます。
^は行の先頭です。式 ^Hello は、行の先頭でのみ「Hello」を見つけます。
$ — 行の終わり。パターン world$ は、最後にのみ「world」を見つけます。
*は、前の文字が0回以上繰り返されることを意味します。たとえば、go*gleは「ggle」、「gogle」、「google」、「goooogle」を検索します。
+ — 1回以上の繰り返し。パターン go+gle は「gogle」、「google」を見つけますが、「ggle」は見つけません。
? —ゼロまたは1つのエントリ。式 colou?r は、「color」と「colour」の両方を検索します。
[]は文字クラスです。パターン [aeiou] は任意の母音を検出し、[0-9] は任意の数字を検出します。
|は論理ORです。式 cat|dog は、「cat」または「dog」のいずれかを見つけます。
() — グループ化。括弧を使用すると、サブ式を作成してテキストの必要な部分をキャプチャできます。
実例
開発者が直面する実際の課題を見てみましょう。
メールアドレスの検証:
import re
def validate_email(email):
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
return bool(re.match(pattern, email))
print(validate_email("user@example.com")) # True
print(validate_email("invalid.email")) # Falseこの式は、メールの基本構造である@の前の文字、ドメイン名、ゾーンをチェックします。
電話番号の抽出:
text = "Contacts: +7(999)123-45-67, 8-800-555-35-35"
pattern = r'[\+\d][\d\-\(\)]{9,}'
phones = re.findall(pattern, text)
print(phones) # ['+7(999)123-45-67', '8-800-555-35-35']機密データの置換:
text = "My card number: 1234-5678-9012-3456"
pattern = r'\d{4}-\d{4}-\d{4}-\d{4}'
masked = re.sub(pattern, '****-****-****-****', text)
print(masked) # 私のカード番号:****-****-****-****URLの解析:
url = "https://example.com:8080/path/to/page?param=value#section"
pattern = r'(?P<protocol>https?://)?(?P<domain>[^:/]+)(?::(?P<port>\d+))?(?P<path>/[^?#]*)?(?:\?(?P<query>[^#]*))?(?:#(?P<fragment>.*))?'
match = re.match(pattern, url)
print(match.group('domain')) # example.com
print(match.group('port')) # 8080高度なテクニック
Lookaheadとlookbehind — テキストをキャプチャせずにコンテキスト検索を行うための強力なツールです。
ポジティブルックアヘッド (?=...) — 次に特定のパターンが続くことを確認します。
# 感嘆符の後に続く単語を探す
pattern = r'\w+(?=!)'
text = "Hi! How are you? Great!"
print(re.findall(pattern, text)) # [「こんにちは」、「素晴らしい」]]ネガティブルックアヘッド (?!...) — 次にパターンが来ないことを確認します。
# ルーブル記号の後に続かない数字を検索する
pattern = r'\d+(?!₽)'
text = "100₽, 200, 300₽, 400"
print(re.findall(pattern, text)) # ['200', '400']貪欲な量子子と怠惰な量子子:
デフォルトでは、量子子は貪欲です。つまり、テキストの最大値をキャプチャします。
text = "<div>First</div><div>Second</div>"
pattern = r'<div>.*</div>'
print(re.findall(pattern, text))
# ['<div>最初</div><div>2番目</div>'] # すべてをキャプチャしました!?を追加して、量子化子を遅延します。
pattern = r'<div>.*?</div>'
print(re.findall(pattern, text))
# ['<div>最初</div>', '<div>2番目</div>'] # 2つの個別の一致名前付きグループ コードを読みやすくします。
log = "2024-11-25 14:30:15 ERROR Database connection failed"
pattern = r'(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>\d{2}:\d{2}:\d{2}) (?P<level>\w+) (?P<message>.*)'
match = re.match(pattern, log)
print(match.group('level')) # ERROR
print(match.group('message')) # Database connection failed
パフォーマンスと最適化
正規表現は、誤って使用すると低速になる可能性があります。いくつかのヒントを次に示します。
複数回使用するためにパターンをコンパイルします。
# 悪い - 毎回コンパイルされる
for text in texts:
if re.match(r'\d{3}-\d{3}', text):
process(text)
# 良い — 一度コンパイル
pattern = re.compile(r'\d{3}-\d{3}')
for text in texts:
if pattern.match(text):
process(text)壊滅的なバックトラッキングを避ける:
一部のパターンは、実行時間の急激な増加を引き起こす可能性があります。
# 危険です!
pattern = r'(a+)+'
text = "aaaaaaaaaaaaaaaaaaaaaaaX" # フリーズすることがありますこれは、失敗した場合の複数のマッチングパスが原因です。解決策は、サポートされている場合は、原子グループまたは所有格限定記号を使用することです。
アンカーを使用する:
# 遅く
pattern = r'\d{4}-\d{2}-\d{2}'
# 行頭の日付の方が速い
pattern = r'^\d{4}-\d{2}-\d{2}'正規表現のデバッグ
正規表現は理解するのが難しい場合があります。視覚化とテストのためのツールを使用してください。
regex101.com — 説明付きの優れたオンラインツール
debuggex.com — パターンをグラフで可視化
regexr.com — ヒント付きインタラクティブサンドボックス
複雑な式の場合は、re.VERBOSEフラグのコメントを追加します。
pattern = re.compile(r'''
^ # Start of line
(?P<username> # Group for username
[a-zA-Z0-9_]{3,16} # From 3 to 16 alphanumeric characters
)
@ # Symbol @
(?P<domain> # Group for domain
[a-zA-Z0-9.-]+ # Domain name
\.[a-zA-Z]{2,} # Point and zone
)
$ # End of line
'正規表現の代替
正規表現は必ずしも最良の解決策ではありません。一部のタスクでは、専用ツールを使用することをお勧めします。
HTMLのパースには、regexの代わりにBeautifulSoupまたはlxmlを使用してください。HTMLは正規言語ではなく、regexは予測できない結果を生み出す可能性があります。
JSONの解析には、組み込みライブラリを使用します。複雑な文法の場合は、PLY や ANTLR などのパーサージェネレーターを検討してください。
単純なサブストリング検索の場合、文字列メソッドは正規表現よりも高速でわかりやすいことが多いです。
結論
正規表現は、開発者の武器庫にある強力なツールです。時間を節約し、コードをコンパクトにし、そうでなければ数十行のコードを必要とするタスクを解決します。シンプルなパターンから始めて、実際のタスクで練習すると、徐々にこのパターン言語を習得することができます。パフォーマンスを念頭に置き、デバッグツールを使用し、恐れずに実験してください。時間が経つにつれて、正規表現はワークフローの自然な一部になります。
プログラミングをより深く理解し、正規表現などのツールを実際に使用する方法を学びたいですか?アプリケーション コディック 初心者向けのPython、JavaScript、Web開発の構造化コースを提供しています。参加してください Telegramチャンネルプログラミングの道のりを支援する同好者のコミュニティのサポートを受けることができます。
