【保存版】Python 正規表現チートシート|記号一覧と使い方を完全解説!

python

正規表現(Regular Expression、略してregex)とは、文字列のパターンを表現するための特別な文法です。

たとえば、こんな場面で威力を発揮します:

  • 「メールアドレスが正しい形式かチェックしたい」
  • 「テキストから電話番号だけを抽出したい」
  • 「HTMLタグを取り除きたい」
  • 「特定のパターンの文字列を一括で置換したい」

Pythonではreモジュールを使うことで、かんたんに正規表現が扱えます。

この記事では、実用的なパターンをチートシート形式で紹介し、すぐに使える形でお届けします!

スポンサーリンク

基本の使い方(Pythonでの記述方法)

reモジュールのインポート

import re

基本的な使用例

import re

text = "私の電話番号は090-1234-5678です"
pattern = r"\d{3}-\d{4}-\d{4}"

# パターンにマッチする部分を検索
result = re.search(pattern, text)
if result:
    print(result.group())  # → 090-1234-5678
    print("マッチしました!")
else:
    print("マッチしませんでした")

重要なポイント

  • r"":raw文字列。バックスラッシュをそのまま使えるため、正規表現では必須
  • re.search():最初にマッチした1件を取得
  • .group():マッチした文字列を取得

文字クラス(基本的な記号)

基本的な文字クラス

記号意味使用例マッチする例
.任意の1文字(改行以外)a.cabc, a1c, a@c
\d数字(0~9)\d{3}123, 999
\D数字以外\D+abc, こんにちは
\w英数字とアンダースコア\w+hello, test_123
\W英数字とアンダースコア以外\W+@#$, !?
\s空白文字(スペース、タブ、改行)\s+, \t, \n
\S空白文字以外\S+hello, 123

カスタム文字クラス

記号意味使用例マッチする例
[abc]a、b、cのいずれか[abc]+a, abc, cab
[a-z]aからzまでの小文字[a-z]+hello, world
[A-Z]AからZまでの大文字[A-Z]+HELLO, WORLD
[0-9]0から9までの数字[0-9]+123, 999
[^abc]a、b、c以外の文字[^0-9]+hello, こんにちは

実用例

import re

# 英数字のみをチェック
text1 = "hello123"
if re.match(r"^[a-zA-Z0-9]+$", text1):
    print("英数字のみです")

# ひらがなのみをチェック
text2 = "こんにちは"  
if re.match(r"^[ひ-ん]+$", text2):
    print("ひらがなのみです")

# 特殊文字を含むかチェック
text3 = "password@123"
if re.search(r"[!@#$%^&*]", text3):
    print("特殊文字が含まれています")

量詞(繰り返しパターン)

基本的な量詞

記号意味使用例マッチする例
*0回以上の繰り返しab*a, ab, abbb
+1回以上の繰り返しab+ab, abbbaはマッチしない)
?0回または1回ab?a, ababbはマッチしない)
{n}ちょうどn回a{3}aaa
{n,}n回以上a{2,}aa, aaa, aaaa
{n,m}n回以上m回以下a{2,4}aa, aaa, aaaa

貪欲マッチと非貪欲マッチ

記号意味動作
*貪欲マッチできるだけ長くマッチ
*?非貪欲マッチできるだけ短くマッチ
+?非貪欲マッチ1回以上、できるだけ短く
??非貪欲マッチ0回または1回、できるだけ短く

実用例

import re

# HTMLタグの抽出(貪欲 vs 非貪欲)
html = "<div>Hello</div><p>World</p>"

# 貪欲マッチ:全体をマッチしてしまう
greedy = re.search(r"<.*>", html)
print(greedy.group())  # → <div>Hello</div><p>World</p>

# 非貪欲マッチ:最初のタグのみマッチ
non_greedy = re.search(r"<.*?>", html)
print(non_greedy.group())  # → <div>

# 郵便番号の検証
zipcode = "123-4567"
if re.match(r"^\d{3}-\d{4}$", zipcode):
    print("正しい郵便番号です")

アンカー(位置指定)

基本的なアンカー

記号意味使用例説明
^行の先頭^Hello行の最初が”Hello”で始まる
$行の末尾world$行の最後が”world”で終わる
\b単語境界\bcat\b独立した単語”cat”
\B非単語境界\Bcat\B単語の一部としての”cat”
\A文字列の先頭\AStart文字列全体の最初
\Z文字列の末尾End\Z文字列全体の最後

実用例

import re

# 完全一致のチェック
def is_valid_email_simple(email):
    pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    return bool(re.match(pattern, email))

print(is_valid_email_simple("user@example.com"))  # → True
print(is_valid_email_simple("invalid-email"))     # → False

# 単語境界の活用
text = "I have a cat and a catch"
# "cat"という独立した単語のみマッチ
cats = re.findall(r"\bcat\b", text)
print(cats)  # → ['cat']

# 行の先頭・末尾での検索
multiline_text = """
Hello World
Goodbye World
Hello Python
"""
# 各行の先頭が"Hello"で始まる行を検索
hello_lines = re.findall(r"^Hello.*$", multiline_text, re.MULTILINE)
print(hello_lines)  # → ['Hello World', 'Hello Python']

グループ化と選択

グループの種類

記号意味使用例説明
(...)キャプチャグループ(ab)+グループ化して後で参照可能
(?:...)非キャプチャグループ(?:ab)+グループ化するが後で参照不可
(?P<name>...)名前付きグループ(?P<year>\d{4})名前で参照可能
``OR(選択)`cat

実用例

import re

# 日付の抽出と分解
date_text = "今日は2024年6月19日です"
pattern = r"(?P<year>\d{4})年(?P<month>\d{1,2})月(?P<day>\d{1,2})日"

match = re.search(pattern, date_text)
if match:
    print(f"年: {match.group('year')}")    # → 年: 2024
    print(f"月: {match.group('month')}")   # → 月: 6
    print(f"日: {match.group('day')}")     # → 日: 19
    print(f"全体: {match.group(0)}")       # → 全体: 2024年6月19日

# 複数の選択肢
pets = "I have a cat, a dog, and a bird"
animals = re.findall(r"\b(cat|dog|bird|fish)\b", pets)
print(animals)  # → ['cat', 'dog', 'bird']

# 電話番号の分解
phone = "090-1234-5678"
pattern = r"(\d{3})-(\d{4})-(\d{4})"
match = re.match(pattern, phone)
if match:
    area = match.group(1)      # → 090
    exchange = match.group(2)  # → 1234
    number = match.group(3)    # → 5678
    print(f"{area} {exchange} {number}")

Pythonのreモジュール関数

主要な関数

関数用途戻り値
re.search()最初のマッチを検索MatchオブジェクトまたはNone
re.match()文字列の先頭からマッチMatchオブジェクトまたはNone
re.findall()すべてのマッチを検索リスト
re.finditer()すべてのマッチをイテレータでイテレータ
re.sub()置換文字列
re.split()分割リスト
re.compile()パターンのコンパイルPatternオブジェクト

使用例

import re

text = "価格は100円、500円、1000円です。税込み価格は110円、550円、1100円です。"

# 1. re.search() - 最初のマッチのみ
first_price = re.search(r"\d+円", text)
print(first_price.group())  # → 100円

# 2. re.findall() - すべてのマッチをリストで
all_prices = re.findall(r"\d+円", text)
print(all_prices)  # → ['100円', '500円', '1000円', '110円', '550円', '1100円']

# 3. re.finditer() - すべてのマッチをイテレータで
for match in re.finditer(r"\d+円", text):
    print(f"位置 {match.start()}-{match.end()}: {match.group()}")

# 4. re.sub() - 置換
masked_text = re.sub(r"\d+円", "***円", text)
print(masked_text)  # → 価格は***円、***円、***円です。税込み価格は***円、***円、***円です。

# 5. re.split() - 分割
sentence = "apple,banana;orange:grape"
fruits = re.split(r"[,;:]", sentence)
print(fruits)  # → ['apple', 'banana', 'orange', 'grape']

# 6. re.compile() - パターンの再利用
pattern = re.compile(r"\d+円")
prices1 = pattern.findall("100円です")
prices2 = pattern.findall("200円と300円")
print(prices1, prices2)  # → ['100円'] ['200円', '300円']

よく使う実用パターン集

メールアドレス

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

# テスト
emails = [
    "user@example.com",        # Valid
    "test.email+tag@domain.co.jp",  # Valid
    "invalid-email",           # Invalid
    "@domain.com",            # Invalid
]

for email in emails:
    print(f"{email}: {validate_email(email)}")

URLの抽出

import re

def extract_urls(text):
    pattern = r"https?://[^\s<>\"']+"
    return re.findall(pattern, text)

text = """
公式サイト: https://www.example.com
APIドキュメント: https://api.example.com/docs
参考記事: http://blog.example.com/article/123
"""

urls = extract_urls(text)
for url in urls:
    print(url)

電話番号

import re

def format_phone_number(phone):
    # ハイフンや空白を除去
    cleaned = re.sub(r"[-\s()]", "", phone)
    
    # 携帯電話の形式
    mobile_pattern = r"^(070|080|090)(\d{4})(\d{4})$"
    mobile_match = re.match(mobile_pattern, cleaned)
    
    if mobile_match:
        return f"{mobile_match.group(1)}-{mobile_match.group(2)}-{mobile_match.group(3)}"
    
    # 固定電話の形式(簡易版)
    landline_pattern = r"^(\d{2,4})(\d{2,4})(\d{4})$"
    landline_match = re.match(landline_pattern, cleaned)
    
    if landline_match:
        return f"{landline_match.group(1)}-{landline_match.group(2)}-{landline_match.group(3)}"
    
    return "不正な電話番号"

# テスト
phones = [
    "09012345678",
    "090-1234-5678", 
    "03 1234 5678",
    "0312345678"
]

for phone in phones:
    print(f"{phone} → {format_phone_number(phone)}")

パスワードの強度チェック

import re

def check_password_strength(password):
    checks = {
        "長さ8文字以上": len(password) >= 8,
        "大文字を含む": bool(re.search(r"[A-Z]", password)),
        "小文字を含む": bool(re.search(r"[a-z]", password)),
        "数字を含む": bool(re.search(r"\d", password)),
        "特殊文字を含む": bool(re.search(r"[!@#$%^&*(),.?\":{}|<>]", password))
    }
    
    passed = sum(checks.values())
    total = len(checks)
    
    print(f"パスワード: {password}")
    for check, result in checks.items():
        print(f"  {check}: {'✓' if result else '✗'}")
    
    if passed == total:
        return "非常に強い"
    elif passed >= 4:
        return "強い"
    elif passed >= 3:
        return "普通"
    else:
        return "弱い"

# テスト
passwords = [
    "password",
    "Password123",
    "P@ssw0rd!",
    "Str0ng!P@ssw0rd"
]

for pwd in passwords:
    strength = check_password_strength(pwd)
    print(f"強度: {strength}\n")

日本語テキストの処理

import re

def extract_japanese_elements(text):
    patterns = {
        "ひらがな": r"[ひ-ん]+",
        "カタカナ": r"[ア-ン]+", 
        "漢字": r"[一-龯]+",
        "数字": r"\d+",
        "英字": r"[a-zA-Z]+"
    }
    
    results = {}
    for name, pattern in patterns.items():
        matches = re.findall(pattern, text)
        results[name] = matches
    
    return results

text = "私はPythonプログラミングを学習中です。バージョンは3.9を使用しています。"
elements = extract_japanese_elements(text)

for element_type, matches in elements.items():
    print(f"{element_type}: {matches}")

フラグ(オプション)

主要なフラグ

フラグ効果使用例
re.IGNORECASE または re.I大文字小文字を区別しないre.search(r"hello", text, re.I)
re.MULTILINE または re.M^と$が各行で動作re.findall(r"^Hello", text, re.M)
re.DOTALL または re.S.が改行文字にもマッチre.search(r".*", text, re.S)
re.VERBOSE または re.X改行・コメントを許可パターンを見やすく記述

使用例

import re

# 大文字小文字を区別しない検索
text = "Hello WORLD hello world"
matches = re.findall(r"hello", text, re.IGNORECASE)
print(matches)  # → ['Hello', 'hello']

# 複数行モード
multiline_text = """First line
Second line
Third line"""

# 通常モード:文字列全体の先頭のみ
normal_matches = re.findall(r"^.+", multiline_text)
print("通常:", normal_matches)  # → ['First line']

# 複数行モード:各行の先頭
multiline_matches = re.findall(r"^.+", multiline_text, re.MULTILINE)
print("複数行:", multiline_matches)  # → ['First line', 'Second line', 'Third line']

# 詳細モード(VERBOSE)
email_pattern = re.compile(r"""
    ^                       # 文字列の開始
    [a-zA-Z0-9._%+-]+       # ユーザー名部分
    @                       # アットマーク
    [a-zA-Z0-9.-]+          # ドメイン名
    \.                      # ドット
    [a-zA-Z]{2,}            # トップレベルドメイン
    $                       # 文字列の終了
""", re.VERBOSE)

print(email_pattern.match("user@example.com"))  # → Matchオブジェクト

エラー処理とデバッグ

よくあるエラーと対処法

import re

# 1. 無効な正規表現パターン
try:
    pattern = r"[abc"  # 閉じ括弧がない
    re.compile(pattern)
except re.error as e:
    print(f"正規表現エラー: {e}")

# 2. グループ参照エラー
text = "2024-06-19"
pattern = r"(\d{4})-(\d{2})-(\d{2})"
match = re.search(pattern, text)

if match:
    try:
        year = match.group(1)   # OK
        month = match.group(2)  # OK  
        day = match.group(3)    # OK
        # invalid_group = match.group(4)  # IndexError!
        print(f"{year}/{month}/{day}")
    except IndexError as e:
        print(f"グループ参照エラー: {e}")

# 3. None の確認
def safe_regex_search(pattern, text):
    match = re.search(pattern, text)
    if match:
        return match.group()
    else:
        return "マッチしませんでした"

print(safe_regex_search(r"\d+", "abc"))      # → マッチしませんでした
print(safe_regex_search(r"\d+", "abc123"))   # → 123

デバッグのコツ

import re

def debug_regex(pattern, text):
    """正規表現のデバッグヘルパー関数"""
    print(f"パターン: {pattern}")
    print(f"テキスト: {text}")
    print("-" * 40)
    
    # コンパイル可能かチェック
    try:
        compiled_pattern = re.compile(pattern)
        print("✓ パターンは有効です")
    except re.error as e:
        print(f"✗ パターンエラー: {e}")
        return
    
    # マッチ結果
    match = re.search(pattern, text)
    if match:
        print(f"✓ マッチしました: '{match.group()}'")
        print(f"  位置: {match.start()}-{match.end()}")
        
        # グループがある場合
        if match.groups():
            for i, group in enumerate(match.groups(), 1):
                print(f"  グループ{i}: '{group}'")
    else:
        print("✗ マッチしませんでした")
    
    # すべてのマッチ
    all_matches = re.findall(pattern, text)
    if all_matches:
        print(f"すべてのマッチ: {all_matches}")
    
    print()

# デバッグ例
debug_regex(r"\d{3}-\d{4}", "電話番号は090-1234です")
debug_regex(r"(\w+)@(\w+\.\w+)", "メールはuser@example.comです")

高度なテクニック

先読み・後読み

import re

# 先読み(Lookahead)
text = "password123 username456"

# 数字が後に続く単語を検索(数字は含まない)
words_before_numbers = re.findall(r"\w+(?=\d)", text)
print(words_before_numbers)  # → ['password', 'username']

# 数字が後に続かない単語を検索
words_not_before_numbers = re.findall(r"\w+(?!\d)", text)
print(words_not_before_numbers)  # → ['password', 'username']

# 後読み(Lookbehind)
prices = "商品A: $100, 商品B: ¥200, 商品C: $300"

# ドル記号の後の数字のみ抽出
dollar_amounts = re.findall(r"(?<=\$)\d+", prices)
print(dollar_amounts)  # → ['100', '300']

# 円記号の後の数字のみ抽出
yen_amounts = re.findall(r"(?<=¥)\d+", prices)
print(yen_amounts)  # → ['200']

条件付きマッチ

import re

def extract_quotes(text):
    """シングルクォートまたはダブルクォートで囲まれた文字列を抽出"""
    # グループ1で開始クォートをキャプチャし、同じクォートで終了
    pattern = r"""
        (["'])          # グループ1: 開始クォート
        (.*?)           # グループ2: 内容(非貪欲)
        \1              # グループ1と同じクォートで終了
    """
    
    matches = re.findall(pattern, text, re.VERBOSE)
    return [match[1] for match in matches]  # 内容のみ返す

text = 'これは"テスト"で、こちらは\'例\'です'
quotes = extract_quotes(text)
print(quotes)  # → ['テスト', '例']

置換での高度な使用

import re

def smart_replace(text):
    """様々な置換パターンの例"""
    
    # 1. 関数を使った置換
    def capitalize_match(match):
        return match.group().upper()
    
    # 単語の最初の文字を大文字に
    result1 = re.sub(r"\b\w", capitalize_match, "hello world python")
    print(f"1. {result1}")  # → Hello World Python
    
    # 2. グループを使った置換
    date_text = "今日は2024/06/19です"
    result2 = re.sub(r"(\d{4})/(\d{2})/(\d{2})", r"\1年\2月\3日", date_text)
    print(f"2. {result2}")  # → 今日は2024年06月19日です
    
    # 3. 条件付き置換
    def format_number(match):
        num = int(match.group())
        if num > 1000:
            return f"{num:,}"  # カンマ区切り
        else:
            return str(num)
    
    numbers_text = "価格は500円と1500円と25000円です"
    result3 = re.sub(r"\d+", format_number, numbers_text)
    print(f"3. {result3}")  # → 価格は500円と1,500円と25,000円です

smart_replace("test")

パフォーマンスの最適化

コンパイルの活用

import re
import time

# 大量のデータを処理する場合の最適化例
def performance_comparison():
    text_list = ["user@example.com", "invalid-email", "test@domain.co.jp"] * 1000
    pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    
    # 方法1: 毎回コンパイル(遅い)
    start_time = time.time()
    for text in text_list:
        re.match(pattern, text)
    time1 = time.time() - start_time
    
    # 方法2: 事前にコンパイル(速い)
    compiled_pattern = re.compile(pattern)
    start_time = time.time()
    for text in text_list:
        compiled_pattern.match(text)
    time2 = time.time() - start_time
    
    print(f"毎回コンパイル: {time1:.4f}秒")
    print(f"事前コンパイル: {time2:.4f}秒")
    print(f"改善率: {time1/time2:.2f}倍速い")

# performance_comparison()

実践問題とその解答

問題1: ログファイルの解析

import re

def parse_log_line(log_line):
    """アクセスログを解析する"""
    pattern = r'(?P<ip>\d+\.\d+\.\d+\.\d+) - - \[(?P<datetime>[^\]]+)\] "(?P<method>\w+) (?P<path>[^"]*)" (?P<status>\d+) (?P<size>\d+|-)'
    
    match = re.match(pattern, log_line)
    if match:
        return match.groupdict()
    return None

# テスト用のログデータ
sample_log = '192.168.1.1 - - [19/Jun/2024:10:30:45 +0900] "GET /index.html" 200 1234'
result = parse_log_line(sample_log)
if result:
    print("解析結果:")
    for key, value in result.items():
        print(f"  {key}: {value}")

問題2: CSVファイルのパース

import re

def parse_csv_line(csv_line):
    """CSV行を解析(カンマやクォートを考慮)"""
    # カンマ区切りだが、クォート内のカンマは無視
    pattern = r'(?:^|,)("(?:[^"]|"")*"|[^,]*)'
    
    matches = re.findall(pattern, csv_line)
    
    # クォートを除去し、エスケープされたクォートを復元
    fields = []
    for match in matches:
        field = match.strip('"')
        field = field.replace('""', '"')
        fields.append(field)
    
    return fields

# テスト
csv_data = [
    'name,age,city',
    'John,25,Tokyo',
    '"Smith, Jr.",30,"New York"',
    '"He said ""Hello""",45,Osaka'
]

for line in csv_data:
    parsed = parse_csv_line(line)
    print(f"入力: {line}")
    print(f"結果: {parsed}")
    print()

問題3: マークダウンテキストの処理

import re

def parse_markdown_headers(markdown_text):
    """マークダウンのヘッダーを抽出して目次を作成"""
    header_pattern = r'^(#{1,6})\s+(.+)
    
    headers = []
    for line_num, line in enumerate(markdown_text.split('\n'), 1):
        match = re.match(header_pattern, line)
        if match:
            level = len(match.group(1))  # #の数
            title = match.group(2).strip()
            headers.append({
                'level': level,
                'title': title,
                'line': line_num
            })
    
    return headers

def generate_toc(headers):
    """目次を生成"""
    toc = []
    for header in headers:
        indent = "  " * (header['level'] - 1)
        anchor = re.sub(r'[^\w\s-]', '', header['title']).strip()
        anchor = re.sub(r'[\s]+', '-', anchor).lower()
        toc.append(f"{indent}- [{header['title']}](#{anchor})")
    
    return '\n'.join(toc)

# テスト用マークダウン
markdown_sample = """# メインタイトル
## 第1章
### セクション1.1
### セクション1.2
## 第2章
# まとめ"""

headers = parse_markdown_headers(markdown_sample)
toc = generate_toc(headers)
print("抽出されたヘッダー:")
for header in headers:
    print(f"  レベル{header['level']}: {header['title']} (行{header['line']})")

print("\n生成された目次:")
print(toc)

トラブルシューティング

よくある間違いと解決策

import re

def common_mistakes_examples():
    """よくある間違いとその解決策"""
    
    print("=== よくある間違い例 ===\n")
    
    # 1. エスケープ忘れ
    print("1. エスケープ忘れ")
    text = "価格は100$です"
    
    # 間違い: $は特殊文字
    try:
        wrong = re.search(r"100$", text)  # 行末の100を探してしまう
        print(f"  間違い結果: {wrong}")
    except:
        pass
    
    # 正解: $をエスケープ
    correct = re.search(r"100\$", text)
    print(f"  正解結果: {correct.group() if correct else 'None'}")
    print()
    
    # 2. 貪欲マッチの問題
    print("2. 貪欲マッチの問題")
    html = "<b>太字</b>と<i>斜体</i>"
    
    # 間違い: 貪欲マッチで全体を取得
    wrong = re.search(r"<.*>", html)
    print(f"  間違い結果: {wrong.group() if wrong else 'None'}")
    
    # 正解: 非貪欲マッチまたは否定文字クラス
    correct1 = re.search(r"<.*?>", html)
    correct2 = re.search(r"<[^>]*>", html)
    print(f"  正解結果1: {correct1.group() if correct1 else 'None'}")
    print(f"  正解結果2: {correct2.group() if correct2 else 'None'}")
    print()
    
    # 3. 文字クラスの間違い
    print("3. 文字クラスの間違い")
    text = "a-z"
    
    # 間違い: ハイフンがレンジとして解釈される
    try:
        wrong = re.search(r"[a-z]", text)  # aからzの任意の文字
        print(f"  間違い(意図と異なる): {wrong.group() if wrong else 'None'}")
    except:
        pass
    
    # 正解: ハイフンをエスケープまたは末尾に配置
    correct1 = re.search(r"[a\-z]", text)
    correct2 = re.search(r"[az-]", text)
    print(f"  正解結果1: {correct1.group() if correct1 else 'None'}")
    print(f"  正解結果2: {correct2.group() if correct2 else 'None'}")

common_mistakes_examples()

パフォーマンス問題の診断

import re
import time

def diagnose_performance_issues():
    """パフォーマンス問題の診断と解決"""
    
    # 問題のあるパターン例
    problematic_text = "a" * 1000 + "b"
    
    print("=== パフォーマンス問題の例 ===\n")
    
    # 1. 破滅的バックトラッキング
    print("1. 破滅的バックトラッキング")
    problematic_pattern = r"(a+)+b"  # 危険なパターン
    
    print("  注意: 以下のパターンは非効率的です")
    print(f"  パターン: {problematic_pattern}")
    print("  → 大量のバックトラッキングが発生する可能性")
    
    # より効率的な代替案
    efficient_pattern = r"a+b"
    print(f"  改善案: {efficient_pattern}")
    print()
    
    # 2. 不必要なキャプチャグループ
    print("2. 不必要なキャプチャグループ")
    text_list = ["test@example.com"] * 1000
    
    # 重いパターン(不要なキャプチャ)
    heavy_pattern = r"([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})"
    
    # 軽いパターン(非キャプチャグループ)
    light_pattern = r"(?:[a-zA-Z0-9._%+-]+)@(?:[a-zA-Z0-9.-]+)\.(?:[a-zA-Z]{2,})"
    
    # 単純にマッチするだけなら
    simple_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
    
    print("  用途に応じてパターンを選択:")
    print(f"  データ抽出用: {heavy_pattern}")
    print(f"  検証のみ: {simple_pattern}")

diagnose_performance_issues()

国際化対応

Unicode文字の処理

import re

def unicode_examples():
    """Unicode文字の処理例"""
    
    # 日本語文字の分類
    text = "私はPythonエンジニアです。年齢は25歳、給料は¥500,000です。"
    
    patterns = {
        "ひらがな": r"[\u3041-\u3096]+",
        "カタカナ": r"[\u30A1-\u30F6]+", 
        "漢字": r"[\u4E00-\u9FAF]+",
        "全角数字": r"[0-9]+",
        "半角数字": r"[0-9]+",
        "全角英字": r"[A-Za-z]+",
        "半角英字": r"[A-Za-z]+",
        "通貨記号": r"[¥¥$€£]+",
    }
    
    print("=== Unicode文字の分類 ===")
    print(f"対象テキスト: {text}\n")
    
    for name, pattern in patterns.items():
        matches = re.findall(pattern, text)
        if matches:
            print(f"{name}: {matches}")
    
    print()
    
    # 絵文字の処理
    emoji_text = "今日は良い天気ですね!😊🌞 Pythonの勉強をします📚💻"
    
    # 絵文字のパターン(簡易版)
    emoji_pattern = r"[\U0001F600-\U0001F64F]|[\U0001F300-\U0001F5FF]|[\U0001F680-\U0001F6FF]|[\U0001F1E0-\U0001F1FF]"
    
    emojis = re.findall(emoji_pattern, emoji_text)
    print("=== 絵文字の抽出 ===")
    print(f"テキスト: {emoji_text}")
    print(f"絵文字: {emojis}")
    
    # 絵文字を除去
    clean_text = re.sub(emoji_pattern, "", emoji_text)
    print(f"絵文字除去後: {clean_text.strip()}")

unicode_examples()

多言語対応

import re

def multilingual_patterns():
    """多言語対応のパターン例"""
    
    # 各言語の文字パターン
    language_patterns = {
        "日本語": {
            "ひらがな": r"[\u3041-\u3096]",
            "カタカナ": r"[\u30A1-\u30F6]", 
            "漢字": r"[\u4E00-\u9FAF]",
            "全体": r"[\u3041-\u3096\u30A1-\u30F6\u4E00-\u9FAF]"
        },
        "韓国語": {
            "ハングル": r"[\uAC00-\uD7AF]",
            "全体": r"[\uAC00-\uD7AF]"
        },
        "中国語": {
            "簡体字": r"[\u4E00-\u9FFF]",
            "全体": r"[\u4E00-\u9FFF]"
        },
        "アラビア語": {
            "基本": r"[\u0600-\u06FF]",
            "全体": r"[\u0600-\u06FF]"
        },
        "ロシア語": {
            "キリル文字": r"[\u0400-\u04FF]",
            "全体": r"[\u0400-\u04FF]"
        }
    }
    
    # テストテキスト
    multilingual_text = "Hello こんにちは 안녕하세요 你好 مرحبا Привет"
    
    print("=== 多言語テキストの分析 ===")
    print(f"テキスト: {multilingual_text}\n")
    
    for language, patterns in language_patterns.items():
        pattern = patterns["全体"]
        matches = re.findall(pattern + "+", multilingual_text)
        if matches:
            print(f"{language}: {matches}")

multilingual_patterns()

実用的なライブラリと組み合わせ

pandasとの組み合わせ

import re
import pandas as pd

def pandas_regex_examples():
    """pandasと正規表現の組み合わせ例"""
    
    # サンプルデータ
    data = {
        'name': ['田中太郎', '佐藤花子', 'John Smith', 'Mary Johnson'],
        'email': ['tanaka@example.com', 'sato@test.co.jp', 'john@domain.org', 'mary@invalid'],
        'phone': ['090-1234-5678', '03-1234-5678', '080-9999-0000', 'invalid-phone'],
        'text': ['価格は1000円です', 'セール中!500円!', '定価2500円→1800円', '無料サンプル']
    }
    
    df = pd.DataFrame(data)
    
    print("=== 元データ ===")
    print(df)
    print()
    
    # 1. メールアドレスの検証
    email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
    df['valid_email'] = df['email'].str.match(email_pattern)
    
    # 2. 電話番号からハイフンを除去
    df['phone_clean'] = df['phone'].str.replace(r'[-\s]', '', regex=True)
    
    # 3. テキストから数字を抽出
    df['numbers'] = df['text'].str.findall(r'\d+')
    
    # 4. 名前から姓と名を分離(日本語名のみ)
    japanese_name_pattern = r'^([^\s]+)([^\s]+)
    df['surname'] = df['name'].str.extract(japanese_name_pattern)[0]
    df['given_name'] = df['name'].str.extract(japanese_name_pattern)[1]
    
    print("=== 処理後データ ===")
    print(df[['name', 'valid_email', 'phone_clean', 'numbers', 'surname', 'given_name']])

# pandas_regex_examples()

よく使うパターンのまとめ

# 保存版:よく使う正規表現パターン集
COMMON_PATTERNS = {
    # 基本的な形式
    'email': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,},
    'url': r'https?://[^\s<>"\']+',
    'ipv4': r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b',
    
    # 日本固有
    'zipcode_jp': r'\d{3}-\d{4}',
    'phone_mobile_jp': r'(070|080|090)-\d{4}-\d{4}',
    'phone_landline_jp': r'\d{2,4}-\d{2,4}-\d{4}',
    
    # 日付・時刻
    'date_yyyy_mm_dd': r'\d{4}[-/]\d{1,2}[-/]\d{1,2}',
    'time_hh_mm': r'[0-2]?[0-9]:[0-5][0-9]',
    'datetime_iso': r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}',
    
    # 数値
    'integer': r'-?\d+',
    'decimal': r'-?\d+(\.\d+)?',
    'currency_jp': r'[¥¥]?[\d,]+円?',
    
    # パスワード(基本)
    'password_strong': r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,},
    
    # HTML/XML
    'html_tag': r'<[^>]+>',
    'html_comment': r'<!--.*?-->',
    
    # プログラミング
    'variable_name': r'[a-zA-Z_][a-zA-Z0-9_]*',
    'hex_color': r'#[0-9a-fA-F]{6}',
    
    # 日本語文字
    'hiragana': r'[\u3041-\u3096]+',
    'katakana': r'[\u30A1-\u30F6]+',
    'kanji': r'[\u4E00-\u9FAF]+',
    'japanese': r'[\u3041-\u3096\u30A1-\u30F6\u4E00-\u9FAF]+',
}

# 使用例関数
def validate_with_common_patterns(text, pattern_name):
    """共通パターンを使用した検証"""
    if pattern_name in COMMON_PATTERNS:
        pattern = COMMON_PATTERNS[pattern_name]
        return bool(re.match(pattern, text))
    else:
        return False

# テスト
test_data = [
    ('user@example.com', 'email'),
    ('123-4567', 'zipcode_jp'),
    ('090-1234-5678', 'phone_mobile_jp'),
    ('2024-06-19', 'date_yyyy_mm_dd'),
    ('こんにちは', 'hiragana'),
]

for data, pattern_name in test_data:
    result = validate_with_common_patterns(data, pattern_name)
    print(f"{data} → {pattern_name}: {'✓' if result else '✗'}")

コメント

タイトルとURLをコピーしました