Pythonで文字列の処理を行う際、単純な置換であればstr.replaceメソッドで十分ですが、複雑なパターンに基づいた置換を行うには正規表現が不可欠です。
Pythonの標準ライブラリであるreモジュールに含まれるre.sub関数を使用することで、正規表現を用いた高度な文字列置換が可能になります。
本記事では、re.subの基本的な使い方から、後方参照を利用した応用的なテクニック、さらには関数を用いた動的な置換方法まで詳しく解説します。
プログラミングの実務において、ログの整形やスクレイピングデータのクリーニングなど、正規表現による置換は非常に多くの場面で役立ちます。
この記事を通じて、re.subの仕様をマスターし、効率的な文字列操作を実現しましょう。
re.subの基本概念と構文
re.subは、指定した正規表現パターンに一致する部分を、別の文字列に置き換えるための関数です。
まずは、基本的な構文を確認しておきましょう。
import re
# 基本的な構文
# re.sub(pattern, repl, string, count=0, flags=0)
各引数の役割を理解することが、re.subを使いこなす第一歩となります。
引数の詳細解説
re.subには、最大で5つの引数を渡すことができます。
| 引数名 | 説明 |
|---|---|
pattern | 検索する正規表現パターンを指定します。 |
repl | 置換後の文字列、または関数を指定します。 |
string | 置換対象となる元の文字列を指定します。 |
count | 置換する最大回数を指定します(デフォルトは0で、すべて置換)。 |
flags | 正規表現の動作を制御するフラグ(re.IGNORECASEなど)を指定します。 |
もっとも重要なのは、最初の3つの引数です。
patternには、どのような文字列を探したいかを正規表現で記述します。
replには、見つかったパターンを何に書き換えたいかを記述します。
そしてstringには、処理を施したい対象のテキストデータを渡します。
基本的な置換の実装方法
それでは、具体的なコードを用いて基本的な置換の方法を見ていきましょう。
正規表現を使わなくても済むような単純な例から、正規表現ならではのパターンマッチングまでを解説します。
固定文字列パターンの置換
まずは、特定の固定された文字列を置換する例です。
import re
text = "Apple, Orange, Apple, Banana"
# "Apple"を"Strawberry"に置換します
result = re.sub(r"Apple", "Strawberry", text)
print(result)
Strawberry, Orange, Strawberry, Banana
この例では、文字列内に存在するすべての「Apple」が「Strawberry」に置き換わっています。
正規表現のパターンを指定する際は、エスケープシーケンスの影響を避けるためにraw文字列(r”…”)を使用するのが一般的です。
正規表現メタ文字を利用した置換
次に、正規表現のメタ文字を使用して、特定の条件に合う複数のパターンを一度に置換してみましょう。
例えば、文章内のすべての「数字」を一括で特定の記号に置き換えたい場合を想定します。
import re
text = "2023年に公開され、2026年に更新されました。"
# 1文字以上の数字(\d+)を"YEAR"という文字列に置換します
result = re.sub(r"\d+", "YEAR", text)
print(result)
YEAR年に公開され、YEAR年に更新されました。
このように、\d+というパターンを使うことで、具体的な数値が何であっても「連続した数字」として認識し、一括で置換することができます。
これは、機密情報のマスク処理やデータの正規化において非常に有効な手法です。
後方参照(Backreference)による高度な置換
re.subの真骨頂は、「見つかった文字列の一部を再利用して置換する」という後方参照の機能にあります。
後方参照を使うことで、「マッチした内容を活かしつつ、順序を入れ替える」といった複雑な操作が可能になります。
グループ化と番号による参照
正規表現内で丸括弧 () を使用すると、その部分はグループとしてキャプチャされます。
置換後の文字列(repl)の中で、\1、\2 と記述することで、キャプチャした内容を呼び出すことができます。
import re
text = "2026/05/20"
# (年)/(月)/(日)の形式を、(日)-(月)-(年)の形式に変換します
# \1=年, \2=月, \3=日 を表します
pattern = r"(\d{4})/(\d{2})/(\d{2})"
replacement = r"\3-\2-\1"
result = re.sub(pattern, replacement, text)
print(result)
20-05-2026
このコードでは、日付の形式をスラッシュ区切りからハイフン区切りに変更しつつ、要素の順番を入れ替えています。
\3が3番目のグループ(日)、\2が2番目のグループ(月)、\1が1番目のグループ(年)に対応しています。
名前付きグループによる参照 (\g<name>)
グループの数が増えると、番号で指定するのは間違いのもとになります。
そのような場合は、(?P<name>...) という構文を使ってグループに名前を付けることが推奨されます。
置換後の文字列では、\g<name> という形式で参照します。
import re
text = "User: Tanaka, ID: 999"
# ユーザー名とIDに名前を付けてキャプチャします
pattern = r"User: (?P<username>\w+), ID: (?P<userid>\d+)"
# 名前付きグループを利用して置換文字列を作成します
replacement = r"ID番号\g<userid>の\g<username>様"
result = re.sub(pattern, replacement, text)
print(result)
ID番号999のTanaka様
この方法は、コードの可読性を劇的に向上させます。
特に複雑な正規表現を扱うチーム開発では、名前付きグループの使用がベストプラクティスとなります。
置換引数に関数(Callable)を渡す動的な置換
re.subの第2引数(repl)には、文字列だけでなく「関数」を渡すことも可能です。
これにより、マッチした内容に応じて計算を行ったり、条件分岐を加えたりする動的な置換が実現できます。
マッチオブジェクトの処理
関数を渡した場合、その関数には引数として「マッチオブジェクト」が1つ渡されます。
関数の戻り値として返した文字列が、実際の置換後の文字列として採用されます。
import re
def uppercase_match(match):
# マッチした文字列を取得し、大文字に変換して返します
return match.group(0).upper()
text = "python is a powerful language."
# 小文字の単語を見つけ、それを大文字にします
result = re.sub(r"python|language", uppercase_match, text)
print(result)
PYTHON is a powerful LANGUAGE.
この機能は、単なる文字列の入れ替え以上の処理が必要な場合に非常に強力です。
ラムダ式を用いた簡潔な記述
簡単な処理であれば、名前付きの関数を定義せずにlambda(ラムダ式)を使うとコードがスッキリします。
import re
text = "価格は 1000 円、送料は 500 円です。"
# 数字を見つけ、消費税10%を加算した数値に置換します
result = re.sub(r"\d+", lambda m: str(int(m.group(0)) * 1.1), text)
print(result)
価格は 1100.0 円、送料は 550.0 円です。
このように、置換のタイミングで計算を行うことができるため、データの加工処理が非常に柔軟になります。
オプション引数とフラグの活用
re.subの動作をより細かく制御するために、count引数やflags引数についても理解を深めましょう。
置換回数の制限(count)
デフォルトでは、一致するすべての箇所が置換されますが、count引数を指定することで、最初の数回だけを置換対象にすることができます。
import re
text = "aaa bbb aaa ccc aaa"
# 最初の2回だけ"aaa"を"zzz"に置換します
result = re.sub(r"aaa", "zzz", text, count=2)
print(result)
zzz bbb zzz ccc aaa
3つ目の「aaa」が置換されずに残っていることがわかります。
フラグによる検索動作の変更
flags引数を使用することで、大文字小文字の区別を無視したり、複数行モードを有効にしたりできます。
代表的なフラグは以下の通りです。
re.IGNORECASE(またはre.I): 大文字小文字を区別せずにマッチングを行います。re.MULTILINE(またはre.M): 各行の先頭と末尾を^や$で扱えるようにします。re.DOTALL(またはre.S):.(ドット)が改行コード\nにもマッチするようにします。
import re
text = "Python, python, PYTHON"
# 大文字小文字を無視してすべての"python"を置換します
result = re.sub(r"python", "JS", text, flags=re.IGNORECASE)
print(result)
JS, JS, JS
フラグを適切に使用することで、複雑な正規表現パターンを書かずに済む場合が多いです。
パフォーマンスの最適化:re.compileの併用
大量のテキストに対して繰り返し同じパターンで置換を行う場合、パターンをあらかじめコンパイルしておくことでパフォーマンスを向上させることができます。
re.compileを使用すると、正規表現オブジェクトが生成され、そのオブジェクトのsubメソッドを呼び出すことができます。
import re
# パターンをコンパイルします
pattern_regex = re.compile(r"\d{3}-\d{4}")
text_list = ["123-4567", "987-6543", "000-1111"]
# コンパイル済みのオブジェクトを使って置換を行います
processed_list = [pattern_regex.sub("郵便番号", t) for t in text_list]
print(processed_list)
['郵便番号', '郵便番号', '郵便番号']
数千、数万行のデータをループで処理するようなケースでは、事前にコンパイルしておく方法が最も効率的です。
よくあるミスと注意点
re.subを使用する際に陥りやすい罠がいくつかあります。
一つは、バックスラッシュの扱いです。
正規表現自体にバックスラッシュが含まれる場合、Pythonの通常の文字列ではエスケープが必要になり、"\\"のように書かなければなりません。
これを避けるために、パターンと置換文字列のどちらにもraw文字列(r””)を使用する習慣をつけましょう。
また、str.replaceで済むような単純な置換(例えば単一の文字の入れ替えなど)にre.subを使うと、処理速度が低下します。
処理内容の複雑さに応じて、適切なメソッドを選択することが大切です。
さらに、正規表現の「欲張り(greedy)」な挙動にも注意が必要です。
例えば、<.*> というパターンでHTMLタグを置換しようとすると、最初から最後までを一気にマッチさせてしまい、意図しない範囲まで置換されてしまうことがあります。
必要に応じて、非欲張りマッチ <.*?> を活用するようにしてください。
まとめ
Pythonのre.sub関数は、正規表現を活用した柔軟で強力な文字列置換ツールです。
基本的な置換から始まり、後方参照を用いたパターンの再構成、そして関数を用いた動的な処理まで、その活用範囲は非常に多岐にわたります。
今回の記事で紹介した、\1や\g<name>による後方参照や、re.compileによる高速化、ラムダ式による動的置換を組み合わせることで、ほとんどの文字列操作課題を解決できるようになるでしょう。
まずはシンプルな置換から試し、徐々に正規表現の表現力を高めていくのが上達の近道です。
日々のコーディングにおいて、より正確で効率的なテキスト処理を実現するために、ぜひre.subを積極的に活用してみてください。
