Pythonで文字列を置換(replace, translate, re.sub, re.subn)
Pythonで文字列を置換するには、replace()
やtranslate()
、正規表現reモジュールのre.sub()
, re.subn()
などを使う。スライスで位置を指定して置換することもできる。
いずれの場合も、置換後の文字列として空文字列''
を指定することで、元の文字列を削除する処理としても利用できる。
置換するのではなく、文字列の中から条件を満たす部分文字列を抽出したい場合やその位置を確認したい場合は以下の記事を参照。
大文字と小文字を変換する場合は専用のメソッドが用意されている。
テキストファイルの中身を置換したい場合は、ファイルを文字列として読み込んでから以降で説明する方法で処理して、再度保存すればよい。
文字列を指定して置換: replace
文字列を指定して置換する場合は文字列(str
)のreplace()
メソッドを使う。
第一引数に置換元文字列、第二引数に置換先文字列を指定する。
s = 'one two one two one'
print(s.replace(' ', '-'))
# one-two-one-two-one
置換先文字列を空文字列''
にすると削除される。
print(s.replace(' ', ''))
# onetwoonetwoone
最大置換回数を指定: 引数count
replace()
の第三引数count
に最大置換回数を指定できる。左から順に置換され、最大置換回数を超えると置換されない。
s = 'one two one two one'
print(s.replace('one', 'XXX'))
# XXX two XXX two XXX
print(s.replace('one', 'XXX', 2))
# XXX two XXX two one
複数の文字列を置換
複数の文字列を同じ文字列に置換する場合は後述の正規表現を使う。
複数の文字列をそれぞれ別の文字列に置換するためのメソッドは用意されていないが、replace()
を繰り返し適用することで実現できる。
s = 'one two one two one'
print(s.replace('one', 'XXX').replace('two', 'YYY'))
# XXX YYY XXX YYY XXX
replace()
を順番に呼んでいるだけなので、はじめの置換先文字列が以降の置換元文字列を含んでいる場合は、はじめの置換先文字列も置換される。順番に注意。
print(s.replace('one', 'XtwoX').replace('two', 'YYY'))
# XYYYX YYY XYYYX YYY XYYYX
print(s.replace('two', 'YYY').replace('one', 'XtwoX'))
# XtwoX YYY XtwoX YYY XtwoX
複数の文字(長さ1の文字列)を置換する場合はtranslate()
メソッドが使える。後述。
文字列をスワップ(交換)
文字列中に含まれる2つの文字列をスワップ(交換・入れ替え)したい場合も上述のように順番に置換していくとうまくいかない。
s = 'one two one two one'
print(s.replace('one', 'two').replace('two', 'one'))
# one one one one one
一旦、別の文字列に置換しておく必要がある。
print(s.replace('one', 'X').replace('two', 'one').replace('X', 'two'))
# two one two one two
関数化すると以下の通り。
def swap_str(s_org, s1, s2, temp='*q@w-e~r^'):
return s_org.replace(s1, temp).replace(s2, s1).replace(temp, s2)
print(swap_str(s, 'one', 'two'))
# two one two one two
この方法は簡易的なもので、一時文字列temp
が元の文字列に含まれているとうまくいかない。厳密にするには一時文字列temp
が元の文字列に含まれているかをチェックし、含まれている場合は別の文字列を生成するような処理が必要。例では特に意味のない適当な文字列をデフォルトに設定している。
文字(長さ1の文字列)をスワップする場合は後述のtranslate()
が便利。
改行文字を置換
改行文字が一種類だけの場合はreplace()
の第一引数に指定すればよい。
s_lines = 'one\ntwo\nthree'
print(s_lines)
# one
# two
# three
print(s_lines.replace('\n', '-'))
# one-two-three
Macを含むUnix系OSで使われる改行文字\n
(LF)とWindows系OSで使われる改行文字\r\n
(CR+LF)が混在している場合は注意が必要。\r\n
の中に\n
が含まれているので順番によっては所望の結果が得られない。
どんな改行文字が含まれているか分からない場合は、各種の改行文字で分割したリストを返すsplitlines()
とリストを文字列に連結するjoin()
メソッドを利用するのが安心。
print(s_lines_multi.splitlines())
# ['one', 'two', 'three']
print('-'.join(s_lines_multi.splitlines()))
# one-two-three
文字列の改行に関するそのほかの処理については以下の記事を参照。
複数の文字を指定して置換: translate
基本的な使い方
複数の文字(長さ1の文字列)を指定して置換する場合は文字列(str
)のtranslate()
メソッドを使う。translate()
に指定する変換テーブルはstr.maketrans()
関数で作成する。
str.maketrans()
関数には置換元文字をキー、置換先文字列を値とする辞書を指定する。
置換元文字は1文字(長さ1の文字列)でなければならない。置換先文字列は文字列またはNone
で、None
の場合は対応する置換元文字が削除される。
s = 'one two one two one'
print(s.translate(str.maketrans({'o': 'O', 't': 'T'})))
# One TwO One TwO One
print(s.translate(str.maketrans({'o': 'XXX', 't': None})))
# XXXne wXXX XXXne wXXX XXXne
str.maketrans()
関数には辞書ではなく3つの文字列を引数として指定することもできる。
第一引数には置換元文字を連結した文字列、第二引数には置換先文字を連結した文字列、第三引数には削除する置換元文字列を連結した文字列を指定する。第三引数は省略可能。
print(s.translate(str.maketrans('ot', 'OT', 'n')))
# Oe TwO Oe TwO Oe
この場合、第一引数と第二引数の文字列の長さは一致している必要があり、置換先文字列に長さ2以上の文字列を指定できない。
# print(s.translate(str.maketrans('ow', 'OTX', 'n')))
# ValueError: the first two maketrans arguments must have equal length
文字をスワップ(交換)
translate()
はreplace()
のように順番に置換していくわけはないので、置換元文字が別の置換先文字と一致していても問題ない。特に考慮する必要はなくそのままスワップ可能。
s = 'one two one two one'
print(s.translate(str.maketrans({'o': 't', 't': 'o'})))
# tne owt tne owt tne
print(s.translate(str.maketrans('ot', 'to')))
# tne owt tne owt tne
正規表現で置換: re.sub, re.subn
replace()
やtranslate()
では置換元文字列に完全一致した場合に置換される。完全一致ではなく正規表現にマッチした文字列を置換したい場合はreモジュールのsub()
関数を使う。
基本的な使い方
re.sub()
では第一引数に正規表現パターン、第二引数に置換先文字列、第三引数に処理対象の文字列を指定する。
import re
s = 'aaa@xxx.com bbb@yyy.net ccc@zzz.org'
print(re.sub('[a-z]+@', 'ABC@', s))
# ABC@xxx.com ABC@yyy.net ABC@zzz.org
replace()
と同じく第四引数count
に最大置換回数を指定できる。
print(re.sub('[a-z]+@', 'ABC@', s, 2))
# ABC@xxx.com ABC@yyy.net ccc@zzz.org
re.compile()
で正規表現パターンオブジェクトを作成してsub()
メソッドを実行することも出来る。同じ正規表現パターンで繰り返し処理する場合はこちらのほうが効率的。
p = re.compile('[a-z]+@')
print(p.sub('ABC@', s))
# ABC@xxx.com ABC@yyy.net ABC@zzz.org
reモジュールの詳細は以下の記事を参照。
複数の文字列を同じ文字列に置換
正規表現に詳しくなくても覚えておくと便利なのが以下の2つ。
大括弧[]
で囲むとその中の任意の一文字にマッチする。複数の異なる文字を同じ文字列に置換する場合に使う。
s = 'aaa@xxx.com bbb@yyy.net ccc@zzz.org'
print(re.sub('[xyz]', '1', s))
# aaa@111.com bbb@111.net ccc@111.org
パターンを|
で区切るといずれかのパターンにマッチする。各パターンには正規表現の特殊文字を使うことももちろん可能だが、文字列をそのまま指定してもよい。複数の異なる文字列を同じ文字列に置換する場合に使う。
print(re.sub('com|net|org', 'biz', s))
# aaa@xxx.biz bbb@yyy.biz ccc@zzz.biz
マッチした部分を使って置換
パターンの一部を()
で囲むと、置換先文字列の中で()
で囲んだ部分にマッチする文字列を使用できる。
s = 'aaa@xxx.com bbb@yyy.net ccc@zzz.org'
print(re.sub('([a-z]+)@([a-z]+)', '\\2@\\1', s))
# xxx@aaa.com yyy@bbb.net zzz@ccc.org
print(re.sub('([a-z]+)@([a-z]+)', r'\2@\1', s))
# xxx@aaa.com yyy@bbb.net zzz@ccc.org
\1
が()
にマッチした部分に対応している。()
が複数ある場合は、\2
, \3
...のようにして使う。
''
または""
で囲まれた通常の文字列だと\\1
のように\
をエスケープする必要があるが、r''
のように先頭にr
をつけるraw文字列の場合は\1
でOK。
第二引数にはマッチオブジェクトを引数とする関数も指定できる。より複雑な処理が可能になる。
def func(matchobj):
return matchobj.group(2).upper() + '@' + matchobj.group(1)
print(re.sub('([a-z]+)@([a-z]+)', func, s))
# XXX@aaa.com YYY@bbb.net ZZZ@ccc.org
ラムダ式を使ってもよい。
print(re.sub('([a-z]+)@([a-z]+)', lambda m: m.group(2).upper() + '@' + m.group(1), s))
# XXX@aaa.com YYY@bbb.net ZZZ@ccc.org
マッチオブジェクトの詳細は以下の記事を参照。
置換した部分の個数を取得
re.subn()
は置換処理された文字列と置換した部分の個数とのタプルを返す。
s = 'aaa@xxx.com bbb@yyy.net ccc@zzz.org'
t = re.subn('[a-z]*@', 'ABC@', s)
print(t)
# ('ABC@xxx.com ABC@yyy.net ABC@zzz.org', 3)
print(type(t))
# <class 'tuple'>
print(t[0])
# ABC@xxx.com ABC@yyy.net ABC@zzz.org
print(t[1])
# 3
引数の指定方法などはsub()
と同じ。()
でグルーピングした部分を使ったり、最大置換回数を指定したりできる。
print(re.subn('([a-z]+)@([a-z]+)', r'\2@\1', s, 2))
# ('xxx@aaa.com yyy@bbb.net ccc@zzz.org', 2)
位置を指定して置換・挿入: スライス
位置を指定して置換するメソッドは無いが、スライスで分割して任意の文字列と連結することで指定した位置が置換された新たな文字列を作成できる。
s = 'abcdefghij'
print(s[:4] + 'XXX' + s[7:])
# abcdXXXhij
文字列の長さ(文字数)はlen()
で取得できるので以下のようにも書ける。こちらのほうが間違いは少ない。
- 関連記事: Pythonで文字列の長さ(文字数)を取得
s_replace = 'XXX'
i = 4
print(s[:i] + s_replace + s[i + len(s_replace):])
# abcdXXXhij
単純に分割した文字列の間に別の文字列を連結しているだけなので文字数が一致している必要はない。
print(s[:4] + '-' + s[7:])
# abcd-hij
文字列の任意の位置に別の文字列を挿入して新たな文字列を作成することも可能。
print(s[:4] + '+++++' + s[4:])
# abcd+++++efghij
スライスについての詳細は以下の記事を参照。