Pythonでの文字列置換といえばreplace
ですよね。
99%の場面ではこれで問題ないですが、ごく稀に特殊な文字列置換をしたい場面が発生します。
置換対象の文字列の内容によって置換する文字列を変えたいときに役に立つのがre.sub
です。
長いことPythonを使ってきましたがそんなことができることを知らなくて衝撃を受けたので記事に残しておきます。
いくつか具体的なパターンを見ていきましょう。
以下のパターンでは全て<>
で囲まれた部分を置換していきます。
使用している正規表現<.*?>
は<>
で囲まれた部分に最短一致します。
置換対象文字列から生成
置換後の文字列がマッチした文字列から関数で生成できる値なら、以下のように置換できます。
def repl(match_obj):
# マッチした文字列の前後の`<>`を取り除く
match_str = match_obj.group(0)[1:-1]
idx = int(match_str)
if idx % 15 == 0:
return 'FizzBuzz'
elif idx % 3 == 0:
return 'Fizz'
elif idx % 5 == 0:
return 'Buzz'
else:
return match_str
text = '''when 1 then <1>,
when 3 then <3>,
when 5 then <5>,
when 15 then <15>'''
print(re.sub('<.*?>', repl, text))
出力は以下になります。
関数repl
で<>
内の数値によって置換内容を変えています。
when 1 then 1,
when 3 then Fizz,
when 5 then Buzz,
when 15 then FizzBuzz
辞書を使って置換
辞書でもリストでもいいですが、置換の関数に引数を渡したい場合のパターンです。
置換に使う関数だけをre.sub
に渡すのでそのままだとマッチしたオブジェクトしか引数として受け取れません。
ラムダ式で引数を渡すことができます。
def repl(match_obj, dct):
# マッチした文字列の前後の`<>`を取り除く
match_str = match_obj.group(0)[1:-1]
return dct[match_str]
user_info = {
'name': 'John',
'age': '25',
'country': 'Japan'
}
text = '''My name is <name>.
I am <age> years old.
I am from <country>.
Please call me <name>.'''
print(re.sub('<.*?>', lambda x: repl(x, user_info), text))
My name is John.
I am 25 years old.
I am from Japan.
Please call me John.
日時のフォーマットを置換
さっきとやっていることは同じですね。
ラムダ式で渡しています。
from datetime import datetime
def repl(match_obj, dt):
# マッチした文字列の前後の`<>`を取り除く
match_str = match_obj.group(0)[1:-1]
return dt.strftime(match_str)
dt_now = datetime(2024, 12, 8, 9, 10, 11)
text = '''The time now is <%-H:%M:%S>.
Today is <%A>, <%B %-d>th.'''
print(re.sub('<.*?>', lambda x: repl(x, dt_now), text))
{}
で囲んでいればformat
で普通にできるのでそっちがいいと思いますが、自力でも頑張れます。
%-H
とかで0埋めしない表記ができますが、ここがハイフンでいけるかは環境に依存するようです。
The time now is 9:10:11.
Today is Sunday, December 8th.
おわりに
今回挙げた例だと実感しきれないかもしれないですが、re.sub
を使うことでコードがシンプルになった経験があったのでその感動を伝えたかったというただそれだけです。