Pythonで文字列を置換(replace, translate, re.sub, re.subn)

Modified: | Tags: Python, 文字列, 正規表現

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
source: str_swap.py

一旦、別の文字列に置換しておく必要がある。

print(s.replace('one', 'X').replace('two', 'one').replace('X', 'two'))
# two one two one two
source: str_swap.py

関数化すると以下の通り。

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
source: str_swap.py

この方法は簡易的なもので、一時文字列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
source: str_swap.py

正規表現で置換: 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()で取得できるので以下のようにも書ける。こちらのほうが間違いは少ない。

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

スライスについての詳細は以下の記事を参照。

関連カテゴリー

関連記事