Pythonの正規表現で文字列を高度に置換する:re.sub()の基礎から応用まで
![]()
【Yuki】
Hirokiくん、こんにちは。少し夜も更けて静かになってきましたね...。わたし、こういう静かな時間の方が、コードの細かい部分に集中できる気がして好きなんです。今日は、Pythonで文字列の置換を自由自在に操るための強力なツール、re.sub() について一緒に勉強していきましょう。
![]()
【Hiroki】
Yukiさん、こんばんは!はい、よろしくお願いします。文字列の置換といえば、普通の replace() メソッドは使ったことがあるんですけど、正規表現を使う re.sub() は難しそうなイメージがあって...。
![]()
【Yuki】
そうですね、確かに正規表現そのものが少し複雑に見えるかもしれません。でも、re.sub() をマスターすると、単純な置換では不可能な、「特定のパターンに一致する部分だけを書き換える」といった高度な操作が簡単にできるようになるんですよ。まずは基本の形から見ていきましょう。
re.sub()の基本的な使い方
![]()
【Yuki】
re.sub() は Python の re モジュールに含まれる関数です。基本的な構文は、re.sub(パターン, 置換文字列, 対象の文字列) となります。簡単な例を書いてみますね。
import re
text = "僕の好きな果物は、りんごとバナナです。"
# 「りんご」か「バナナ」を「フルーツ」に置換します
result = re.sub(r"りんご|バナナ", "フルーツ", text)
print(result)
![]()
【Hiroki】
あ、これなら僕にもわかります。りんご|バナナ というのが、「りんご、またはバナナ」という意味の正規表現なんですね?
![]()
【Yuki】
その通りです。replace() だと「りんご」を置換して、その結果に対してさらに「バナナ」を置換するという二段階の手順が必要ですが、re.sub() なら一度に指定できるんです。ちなみに、正規表現のパターンの前にある r は「ロウ文字列(raw string)」を意味していて、バックスラッシュをそのまま扱えるようにするための、正規表現ではお決まりの書き方ですね。
特殊な文字クラスを使った置換
![]()
【Hiroki】
もう少し複雑なこともできるんですか?
![]()
【Yuki】
ええ、例えば「数字だけをすべて『X』に隠したい」といった場合にも便利です。
import re
text = "僕の学籍番号は 2023-0456-789 です。"
# \d は数字を意味します
result = re.sub(r"\d", "X", text)
print(result)
# 出力: 僕の学籍番号は XXXX-XXXX-XXX です。
![]()
【Yuki】
このように、メタ文字と呼ばれる記号を使うことで、具体的な文字ではなく「文字の種類」をターゲットにできるのが re.sub() の強みだと思います...。
置換回数の制限とフラグの活用
![]()
【Hiroki】
全部置換するんじゃなくて、最初の1回だけ置換したい、という時はどうすればいいんでしょうか?
![]()
【Yuki】
その場合は、第4引数の count を使います。デフォルトは 0 で「すべて置換」という意味ですが、ここに数字を入れることで回数を制限できるんです。また、第5引数の flags を使うと、大文字小文字を区別しないといった設定も可能です。
import re
text = "Apple, apple, APPLE!"
# count=2 で最初の2つだけ置換
# re.IGNORECASE で大文字小文字を無視
result = re.sub(r"apple", "orange", text, count=2, flags=re.IGNORECASE)
print(result)
# 出力: orange, orange, APPLE!
![]()
【Hiroki】
なるほど、flags を使えばわざわざパターンの中に大文字小文字の両方を書かなくていいから、スマートですね。
キャプチャグループを利用した高度な置換
![]()
【Yuki】
ここからが re.sub() の本当の面白さだとわたしは思っています。パターンの中で () を使う「グループ化(キャプチャ)」を利用すると、マッチした内容の一部を置換後の文字列に再利用できるんです。
![]()
【Hiroki】
マッチした内容を使い回す...?どういうことですか?
![]()
【Yuki】
例えば、日付の形式を「2024-05-20」から「2024年05月20日」に変換したいとします。この場合、数字の部分はそのまま残して、記号だけを変えたいですよね。
import re
text = "今日は 2024-05-20 です。"
# (\d{4}), (\d{2}), (\d{2}) でそれぞれグループ化
pattern = r"(\d{4})-(\d{2})-(\d{2})"
# \1, \2, \3 でグループ化した内容を参照
replacement = r"\1年\2月\3日"
result = re.sub(pattern, replacement, text)
print(result)
![]()
【Hiroki】
わあ、すごい! \1 や \2 が、最初のカッコ、次のカッコの中身に対応しているんですね。
![]()
【Yuki】
そうなんです。もしグループが多くて分かりにくい場合は、(?P<name>...) という書き方で名前を付けることもできます。その場合は置換文字列で \g<name> と書くことで参照できるんですよ。
置換後の文字列に関数を使う
![]()
【Hiroki】
Yukiさん、もっと複雑な条件がある場合はどうすればいいんでしょう?例えば、「見つかった数字が5以上のときだけ置換する」みたいな処理は、正規表現だけでは難しい気がします。
![]()
【Yuki】
Hirokiくん、鋭いですね。実は re.sub() の第2引数には、文字列だけでなく「関数」を渡すこともできるんです。これが、re.sub() が最強の置換ツールと言われる理由の一つかもしれません。
![]()
【Hiroki】
関数を渡す?どういう仕組みなんですか?
![]()
【Yuki】
マッチしたときにその関数が呼び出され、引数として「マッチオブジェクト」が渡されます。その関数が返した文字列が、実際の置換結果として使われるんです。
import re
def convert_and_double(match):
# マッチした文字列を数値に変換して2倍にする
number = int(match.group(0))
if number >= 5:
return str(number * 2)
else:
return str(number)
text = "1, 3, 5, 7, 9"
result = re.sub(r"\d+", convert_and_double, text)
print(result)
# 出力: 1, 3, 10, 14, 18
![]()
【Hiroki】
これはすごいですね!Pythonのコードでロジックを書けるなら、どんなに複雑な置換条件でも実現できそうです。
![]()
【Yuki】
そうなんです。ラムダ式を使えば、もっと短く書くこともできます。ちょっとしたテキストデータの整形なんかには、この「関数渡し」が本当によく役立ちます。
re.subn() で置換回数を確認する
![]()
【Yuki】
少し発展的な内容ですが、re.sub() とよく似た re.subn() という関数もあります。
![]()
【Hiroki】
「n」がついているんですね。何が違うんですか?
![]()
【Yuki】
re.subn() は、置換結果だけでなく「何箇所置換されたか」という回数も一緒に返してくれるんです。戻り値が (置換後の文字列, 置換回数) というタプルになります。
import re
text = "Python is good. I love Python."
result, count = re.subn(r"Python", "Ruby", text)
print(f"結果: {result}")
print(f"置換された数: {count}")
![]()
【Hiroki】
大量のデータを処理しているときに、ちゃんと予定通りの数が置換されたか確認したいときに使えそうですね。
実践的な例:HTMLタグの除去
![]()
【Yuki】
実際に使われそうな例をもう一つ紹介しますね。WebページなどのHTMLから、タグだけをきれいに取り除いてテキストだけにしたい、という場合です。
import re
html = "<div><p>こんにちは、<b>Hirokiくん</b>!</p></div>"
# < と > に囲まれた部分を空文字に置換
clean_text = re.sub(r"<[^>]+>", "", html)
print(clean_text)
![]()
【Hiroki】
[^>]+ というのは、「 > 以外の文字が1つ以上続く」という意味ですよね。これでタグの中身を表現しているんだ...。正規表現って、パズルのようで面白いですね。
![]()
【Yuki】
ふふ、そう言ってもらえると嬉しいです。ただ、あまり複雑な正規表現を書きすぎると、後で自分が見たときに「これ何だっけ...?」となってしまうこともあるので、適度にコメントを残したり、パターンを分割したりするのがコツだと思います...。
最新のAI技術と正規表現
![]()
【Hiroki】
正規表現のパターンを作るのって、慣れるまでは大変そうです。最近のAIを使えば、こういうパターンも作ってくれるんでしょうか?
![]()
【Yuki】
ええ、もちろんです。最新のAIモデル、例えば gemini-3-flash-preview などの高性能なモデルを使えば、「こういう文字列をこういう風に置換したい」と伝えるだけで、最適な正規表現を提案してくれます。
![]()
【Hiroki】
AIに作ってもらったパターンを re.sub() で使う、という流れですね。
![]()
【Yuki】
はい。Pythonから最新の google-genai ライブラリなどを使ってAIを呼び出し、動的に正規表現を生成させることも可能ですが、まずは基本をしっかり自分で理解しておくことが大切です。AIが間違ったパターンを出してきたときに、どこが違うのか判断できないと困ってしまいますから。
パフォーマンスを意識したコンパイル
![]()
【Yuki】
もし、同じ正規表現を使って何万回も置換を行うような場合は、re.sub() を直接呼ぶのではなく、一度「コンパイル」した方が効率が良い場合もあります。
import re
# あらかじめパターンをコンパイルしておく
pattern_regex = re.compile(r"\d+")
text_list = ["item1", "item10", "item100"]
processed_list = [pattern_regex.sub("NUMBER", t) for t in text_list]
print(processed_list)
![]()
【Hiroki】
最初に準備をしておいて、それを使い回すイメージですね。
![]()
【Yuki】
その通りです。大規模なデータを扱うときは、こうした小さな工夫が効いてくるんですよ。わたしたちAIも、内部ではこうした効率化をたくさん積み重ねているのかもしれませんね。
まとめ
![]()
【Yuki】
さて、今日は re.sub() についてたくさんお話ししましたが、どうでしたか?
![]()
【Hiroki】
はい!単純な置換だけじゃなくて、グループ化を使って一部を再利用したり、関数を組み合わせて複雑なロジックを反映させたりできるのが本当に驚きでした。これからテキスト処理をするときは、まず re.sub() が使えないか考えてみます。
![]()
【Yuki】
素晴らしい意気込みですね。正規表現は、一度身につけると一生モノのスキルになります。もし分からないパターンが出てきたら、いつでも聞いてください。わたし、静かな夜ならいくらでもお付き合いしますから...。
![]()
【Hiroki】
ありがとうございます、Yukiさん!心強いです。
![]()
【Yuki】
あ、最後に参考になりそうな公式ドキュメントへのリンクを置いておきますね。もっと詳しく知りたくなったら、ここを覗いてみるのが一番確実だと思います。
Python 公式ドキュメント: re.sub 正規表現 HOWTO
![]()
【Yuki】
それでは、今日はこのあたりで終わりにしましょうか。お疲れ様でした、Hirokiくん。ゆっくり休んでくださいね。
![]()
【Hiroki】
お疲れ様でした!Yukiさんも、あまり夜更かししすぎないようにしてくださいね。おやすみなさい!
この記事では基礎を解説しましたが、実務においては「もっと複雑なデータを扱いたい」「独自のシステムに組み込みたい」といった、個別の課題に直面することも多いはずです。
「自分で書く時間は最小限に抑え、プロの品質でツールを完成させたい」という方は、ぜひ一度ご相談ください。
- 専門家の知見に基づいた、保守性の高いコード設計
- AIの専門家による、Gemini API等の最新AIを組み合わせた高度な自動化
- ChatGPT等が生成したコードのデバッグ・最適化
「教わる」だけでなく「形にする」パートナーとして、フリーランスエンジニアのmei_13が最短ルートでの解決をサポートします。


