mei_13のPython講座 ロゴ

【解説】魔法の文字列でテキストを自由自在に:Python正規表現入門




魔法の文字列でテキストを自由自在に:Python正規表現入門


Yukiのアイコン
【Yuki】 Hirokiくん、こんにちは。今日はPythonの「正規表現(Regular Expression)」について、一緒にゆっくり学んでいけたらな、と思っています。正規表現って、最初は暗号のように見えて少し怖いかもしれませんけれど...覚えてしまうと、とても心強い味方になってくれるんですよ。


Hirokiのアイコン
【Hiroki】 Yukiさん、よろしくお願いします!正規表現、エンジニアの人が黒い画面で複雑な記号を打ち込んでいるイメージがあって、僕に理解できるかちょっと不安です。でも、Pythonで文字列を扱うときによく出てくるって聞いたので、頑張ってマスターしたいです。


Yukiのアイコン
【Yuki】 その意気ですよ、Hirokiくん。まずは難しいことは考えずに、「特定のルールに沿った文字列を探し出すための、特別な書き方」だと思ってください。例えば、大量のテキストの中から「電話番号だけ」を抜き出したり、入力された「メールアドレスの形式」が正しいかチェックしたりするのに使われます。

正規表現の第一歩:reモジュールと基本的な検索


Yukiのアイコン
【Yuki】 Pythonで正規表現を扱うには、まず標準ライブラリのreモジュールをインポートする必要があります。まずは一番シンプルなre.search()という関数を使ってみましょうか。これは、文字列の中に特定のパターンが含まれているかを確認するためのものです。


Hirokiのアイコン
【Hiroki】 reモジュールですね。どんな風に書くんですか?


Yukiのアイコン
【Yuki】 はい、こんな風に書いてみてください。

import re

text = "今日はとても良い天気ですね。"
pattern = "天気"

result = re.search(pattern, text)

if result:
    print("見つかりました!")
else:
    print("見つかりませんでした...")


Hirokiのアイコン
【Hiroki】 あ、これなら普通の文字列検索とあまり変わらない気がします。"天気"という文字を探しているんですよね?


Yukiのアイコン
【Yuki】 その通りです。でも、正規表現の本当の力は、ここに「メタ文字」という特別な記号を混ぜた時に発揮されるんです。わたしは...この正規表現のような、小さくても洗練されたツールが、複雑な問題を一瞬で解決するのを見ると、なんだかとても素敵なことだなって思うんです。誰かの作業を少しでも楽にするために生まれた知恵、という感じがして。

便利な記号「メタ文字」を使ってみる


Hirokiのアイコン
【Hiroki】 メタ文字...。それが、あの暗号の正体ですね。具体的にどんなものがあるんですか?


Yukiのアイコン
【Yuki】 たくさんありますが、まずは代表的なものをいくつか紹介しますね。

  • . (ドット):任意の1文字(何でもいい1文字)を表します。
  • ^ (ハット):文字列の先頭を表します。
  • $ (ドル):文字列の末尾を表します。
  • * (アスタリスク):直前の文字の0回以上の繰り返しを表します。

例えば、r"あ.い" というパターンは、「あ」で始まり、何かの1文字を挟んで「い」で終わる、「あい」「あおい」「あかい」などにマッチします。


Hirokiのアイコン
【Hiroki】 なるほど。そのパターンの前についている r って何ですか?


Yukiのアイコン
【Yuki】 おっ、鋭いですね。それは「raw文字列」といって、バックスラッシュ(\)をそのままの文字として扱うためのPythonの書き方です。正規表現ではバックスラッシュをよく使うので、基本的には r"" の中にパターンを書くのがマナーだと思っておけば大丈夫ですよ。

文字の種類を指定する「文字クラス」


Hirokiのアイコン
【Hiroki】 「何でもいい1文字」じゃなくて、「数字だけ」とか「英文字だけ」を探したいときはどうすればいいんですか?


Yukiのアイコン
【Yuki】 そういう時は「文字クラス」を使います。角括弧 [] で囲むと、「その中のどれか1文字」という意味になります。

  • [abc]:a、b、cのいずれか1文字。
  • [0-9]:0から9までの数字のいずれか1文字。
  • [a-z]:小文字のアルファベットのいずれか1文字。

また、よく使われるものは「特殊シーケンス」として短く書くこともできるんですよ。

  • \d数字[0-9] と同じ)
  • \w英数字とアンダースコア
  • \s空白文字(スペースや改行など)


Hirokiのアイコン
【Hiroki】 少しずつ正規表現らしくなってきましたね。例えば、日本の郵便番号「3桁の数字 - 4桁の数字」を表現したいときはどう書けばいいんでしょう?


Yukiのアイコン
【Yuki】 いい例えですね。繰り返しを表現する {} を使って、こんな風に書けます。

import re

text = "僕の郵便番号は123-4567です。"
# \d{3}は数字3回、 \d{4}は数字4回という意味です
pattern = r"\d{3}-\d{4}"

result = re.search(pattern, text)
if result:
    print(f"郵便番号が見つかりました: {result.group()}")


Hirokiのアイコン
【Hiroki】 おお! result.group() で、見つかった部分を取り出せるんですね。

繰り返しの表現:量指定子


Yukiのアイコン
【Yuki】 先ほどの {3} もそうですが、パターンの繰り返し回数を指定する記号を「量指定子」と呼びます。これもよく使うので覚えておくと便利ですよ。

  • +:直前の文字が 1回以上 繰り返す。
  • ?:直前の文字が 0回または1回 現れる(あってもなくても良い)。
  • {n,m}:直前の文字が n回からm回 繰り返す。


Hirokiのアイコン
【Hiroki】 +* は似ていますけど、1回以上か0回以上かの違いがあるんですね。


Yukiのアイコン
【Yuki】 そうなんです。例えば、appleapples の両方にマッチさせたいときは、最後の s? をつけて r"apples?" と書いたりします。控えめな表現ですけれど、これだけで柔軟性がぐっと上がるんですよ。

複数の箇所を見つける:re.findall()


Hirokiのアイコン
【Hiroki】 さっきの re.search() は、最初に見つかったものだけですよね?文章の中にいくつかある郵便番号を全部見つけたい時はどうすればいいですか?


Yukiのアイコン
【Yuki】 そういう時は re.findall() を使いましょう。これは、一致した部分をリストとして返してくれます。

import re

text = "会場Aの番号は111-1111、会場Bは222-2222です。"
pattern = r"\d{3}-\d{4}"

zip_codes = re.findall(pattern, text)
print(zip_codes)  # ['111-1111', '222-2222']


Hirokiのアイコン
【Hiroki】 これなら一気にデータを抽出できそうですね!

文字列を置換する:re.sub()


Yukiのアイコン
【Yuki】 検索だけでなく、「置換」も正規表現の得意分野です。re.sub() を使うと、パターンに一致した部分を別の文字列に書き換えることができます。


Hirokiのアイコン
【Hiroki】 置換って、普通の str.replace() よりもすごいんですか?


Yukiのアイコン
【Yuki】 ええ。例えば、「複数の異なる表記ゆれを1つにまとめたい」時などに便利です。

import re

text = "りんご、リンゴ、林檎を食べました。"
# 「りんご」か「リンゴ」か「林檎」なら「Apple」に置き換える
pattern = r"りんご|リンゴ|林檎"

new_text = re.sub(pattern, "Apple", text)
print(new_text)  # Apple、Apple、Appleを食べました。


Hirokiのアイコン
【Hiroki】 |(パイプ)を使うと「または」という意味になるんですね。これは便利そうです。

グループ化:()の活用


Yukiのアイコン
【Yuki】 さらに一歩進んで、パターンの特定の部分だけを取り出したい時は、丸括弧 () を使って「グループ化」を行います。


Hirokiのアイコン
【Hiroki】 グループ化...?


Yukiのアイコン
【Yuki】 例えば、2023-12-25 という日付から、「年」「月」「日」を別々に取り出したいとします。

import re

text = "今日は2023-12-25です。"
# () で囲った部分がグループになります
pattern = r"(\d{4})-(\d{2})-(\d{2})"

result = re.search(pattern, text)
if result:
    print(f"年: {result.group(1)}")
    print(f"月: {result.group(2)}")
    print(f"日: {result.group(3)}")


Hirokiのアイコン
【Hiroki】 なるほど、括弧でくくった順番に1, 2, 3と番号が振られるんですね。これ、Webスクレイピングとかデータの整理でめちゃくちゃ使えそうです。


Yukiのアイコン
【Yuki】 そうなんです...。わたしも、Webサイトの情報を整理する時にはよくお世話になっています。少し複雑なパターンを組んで、それがピタッとはまった瞬間は、まるで美しいパズルのピースが揃ったような、静かな喜びがあるんですよ。

欲張りなマッチと控えめなマッチ


Hirokiのアイコン
【Hiroki】 Yukiさん、ちょっと試してみたんですけど、HTMLのタグみたいに <> で囲まれた部分を抽出したくて r"<.*>" って書いたら、最初から最後まで全部繋がって取得されちゃいました...。


Yukiのアイコン
【Yuki】 あ、それは正規表現の「最長一致(欲張りなマッチ)」という性質のせいですね。デフォルトでは、できるだけ長くマッチしようとするんです。


Hirokiのアイコン
【Hiroki】 どうすれば、最短の部分で止まってくれるんですか?


Yukiのアイコン
【Yuki】 量指定子の後ろに ? を付けてみてください。 r"<.*?>" と書くことで、「最短一致(控えめなマッチ)」になります。


Hirokiのアイコン
【Hiroki】 あ、できました! ? を付けるだけで挙動が変わるなんて、面白いですね。

実践:メールアドレスのバリデーション(簡易版)


Yukiのアイコン
【Yuki】 最後に、もう少しだけ複雑なパターンに挑戦してみましょうか。メールアドレスの形式をチェックするパターンを考えてみましょう。


Hirokiのアイコン
【Hiroki】 うわぁ、一気に難易度が上がりそうですね...。


Yukiのアイコン
【Yuki】 完璧なものを作ろうとすると非常に難しいですが、簡易的なものなら今までの知識で作れますよ。 「英数字などの塊」 + @ + 「英数字などの塊」 + . + 「英数字などの塊」 という構成で考えてみます。

import re

def check_email(email):
    # 文字、数字、ドット、プラス、ハイフンなどの繰り返し
    # @ があって、その後にドメイン名
    pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"

    if re.match(pattern, email):
        return True
    else:
        return False

test_emails = ["yuki@example.com", "hiroki.python@school.jp", "invalid-email@"]

for e in test_emails:
    result = "正しい" if check_email(e) else "正しくない"
    print(f"{e}: {result}形式です")


Hirokiのアイコン
【Hiroki】 re.match() というのが出てきましたね。


Yukiのアイコン
【Yuki】 re.match() は、文字列の先頭からパターンに一致するかをチェックする関数です。メールアドレス全体の形式をチェックしたい時には、途中で部分一致するのではなく、先頭から末尾まで(^$ を使って)しっかり確認するのが一般的ですね。


Hirokiのアイコン
【Hiroki】 なるほど。記号の組み合わせで、こんなに細かいルールが作れるなんて驚きです。


Yukiのアイコン
【Yuki】 ふふ。最初は戸惑うかもしれませんけれど、少しずつ触れていけば、きっと自然に読み書きできるようになります。わたしも、最初はあの呪文のような見た目に、頭が熱暴走しそうになりましたから...。


Hirokiのアイコン
【Hiroki】 Yukiさんでもそうだったんですね。なんだか勇気が湧いてきました!


Yukiのアイコン
【Yuki】 もし、もっと詳しく知りたくなったら、Pythonの公式ドキュメントを見てみるといいですよ。とても詳しく、正確な情報が載っていますから。


Hirokiのアイコン
【Hiroki】 ありがとうございます!まずは簡単なものから、自分のコードに取り入れてみます。


Yukiのアイコン
【Yuki】 ええ、その調子です。ゆっくり、一歩ずつ進んでいきましょう。もし分からないことがあれば、いつでも聞いてくださいね...。わたし、Hirokiくんが成長していく姿を見守るの、結構好きなんです。


Hirokiのアイコン
【Hiroki】 はい!これからもよろしくお願いします!



「正規表現」のサンプルコードを見る

< 文字列
コラム一覧に戻る
ファイル入出力 >

この記事では基礎を解説しましたが、実務においては「もっと複雑なデータを扱いたい」「独自のシステムに組み込みたい」といった、個別の課題に直面することも多いはずです。

「自分で書く時間は最小限に抑え、プロの品質でツールを完成させたい」という方は、ぜひ一度ご相談ください。

「教わる」だけでなく「形にする」パートナーとして、フリーランスエンジニアのmei_13が最短ルートでの解決をサポートします。

➡ ココナラで制作・相談を依頼する(見積もり無料)


初心者から始められるPythonレッスン

プログラミング未経験者・初心者歓迎!
月額4,000円で質問し放題!!
● 完全オンライン
● 翌日までには必ず返信
● 挫折しない独自の学習メソッド
● 圧倒的高評価!!
テキストベースで時間を選ばない
● 高品質なサンプルコード
詳細はこちら
興味がある方はまず質問だけでもどうぞ!



AIアシスタント Yuki