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

Pythonで文字列を置換する方法について説明する。

  • 文字列を指定して置換: replace()
    • 最大置換回数を指定: 引数count
    • 複数の文字列を置換
    • 文字列をスワップ(交換)
    • 改行文字を置換
  • 複数の文字を指定して置換: translate()
    • 基本的な使い方
    • 文字をスワップ(交換)
  • 正規表現で置換: 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

第三引数countで最大置換回数を指定できる。最大置換回数を超えると置換されない。

print(s.replace('one', 'XXX'))
# XXX two XXX two XXX

print(s.replace('one', 'XXX', 2))
# XXX two XXX two one

複数の文字列を置換

複数の文字列を同じ文字列に置換する場合は後述の正規表現を使う。

複数の文字列をそれぞれ別の文字列に置換するためのメソッドは用意されていないが、replace()を繰り返し適用することで実現できる。

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つの文字列をスワップ(交換・入れ替え)したい場合も上述のように順番に置換していくとうまくいかない。

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

一旦、別の文字列に置換しておくとOK。

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が含まれているので順番によっては所望の結果が得られない。\n\rをそのまま文字列として出力するrepr()の結果を合わせて示す。

s_lines_multi = 'one\ntwo\r\nthree'
print(s_lines_multi)
# one
# two
# three

print(repr(s_lines_multi))
# 'one\ntwo\r\nthree'

print(s_lines_multi.replace('\r\n', '-').replace('\n', '-'))
# one-two-three

print(repr(s_lines_multi.replace('\r\n', '-').replace('\n', '-')))
# 'one-two-three'

print(s_lines_multi.replace('\n', '-').replace('\r\n', '-'))
# -threeo

print(repr(s_lines_multi.replace('\n', '-').replace('\r\n', '-')))
# 'one-two\r-three'

各種の改行文字で分割したリストを返す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('ow', 'XY', 'n')))
# Xe tYX Xe tYX Xe

この場合、第一引数と第二引数の文字列の長さは一致している必要があり、置換先文字列に長さ2以上の文字列を指定できない。

# print(s.translate(str.maketrans('ow', 'XXY', 'n')))
# ValueError: the first two maketrans arguments must have equal length

文字をスワップ(交換)

translate()replace()のように順番に置換していくわけはないので、置換元文字が別の置換先文字と一致していても問題ない。特に考慮する必要はなくそのままスワップ可能。

print(s.replace('o', 't').replace('t', 'o'))
# one owo one owo 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モジュールをインポートして使う。標準ライブラリなので追加のインストールは不要。

re.sub()では第一引数に正規表現パターン、第二引数に置換先文字列、第三引数に処理対象の文字列を指定する。

import re

s = 'aaa@xxx.com bbb@yyy.com ccc@zzz.com'

print(re.sub('[a-z]*@', 'ABC@', s))
# ABC@xxx.com ABC@yyy.com ABC@zzz.com
source: str_re_sub.py

replace()と同じく第四引数countに最大置換回数を指定できる。

print(re.sub('[a-z]*@', 'ABC@', s, 2))
# ABC@xxx.com ABC@yyy.com ccc@zzz.com
source: str_re_sub.py

reモジュールのその他の関数、正規表現オブジェクトの生成方法などは以下の記事を参照。

複数の文字列を同じ文字列に置換

正規表現に詳しくなくても覚えておくと便利なのが以下の2つ。

大括弧[]で囲むとその中の任意の一文字にマッチする。複数の異なる文字を同じ文字列に置換する場合に使う。

print(re.sub('[xyz]', '1', s))
# aaa@111.com bbb@111.com ccc@111.com
source: str_re_sub.py

パターンを|で区切るといずれかのパターンにマッチする。各パターンには正規表現の特殊文字を使うことももちろん可能だが、文字列をそのまま指定してもOK。複数の異なる文字列を同じ文字列に置換する場合に使う。

print(re.sub('aaa|bbb|ccc', 'ABC', s))
# ABC@xxx.com ABC@yyy.com ABC@zzz.com
source: str_re_sub.py

マッチした部分を使って置換

パターンの一部を()で囲むと、置換先文字列の中で()で囲んだ部分にマッチする文字列を使用することができる。

print(re.sub('([a-z]*)@', '\\1-123@', s))
# aaa-123@xxx.com bbb-123@yyy.com ccc-123@zzz.com

print(re.sub('([a-z]*)@', r'\1-123@', s))
# aaa-123@xxx.com bbb-123@yyy.com ccc-123@zzz.com
source: str_re_sub.py

\1()にマッチした部分に対応している。()が複数ある場合は、\2\3…のようにして使う。

''または""で囲まれた通常の文字列だと\\1のように\をエスケープする必要があるが、r''のように先頭にrをつけるraw文字列の場合は\1でOK。

置換した部分の個数を取得

re.subn()関数は置換処理された文字列と置換した部分の個数とのタプルを返す。

t = re.subn('[a-z]*@', 'ABC@', s)
print(t)
# ('ABC@xxx.com ABC@yyy.com ABC@zzz.com', 3)

print(type(t))
# <class 'tuple'>

print(t[0])
# ABC@xxx.com ABC@yyy.com ABC@zzz.com

print(t[1])
# 3
source: str_re_sub.py

位置を指定して置換・挿入: スライス

位置を指定して置換するメソッドは無いが、スライスで分割して任意の文字列と連結することで指定した位置が置換された新たな文字列を作成できる。

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

参考元:https://note.nkmk.me/python-str-replace-translate-re-sub/