閉じる

str.translateで複数文字を一括置換する方法とreplaceの違い

大量の文字種を同時に置き換えたいとき、str.translateは強力かつ高速に動作します。

この記事では、初心者の方がつまずきやすい点を避けながら、str.translatestr.maketransの基本から、str.replaceとの違い、実践的な使い分け、そしてベストプラクティスまでを段階的に解説します。

str.translateで複数文字を一括置換する基本

一括置換のメリットと用途

複数の文字をまとめて置換する目的は、処理の正確さと速度を両立することにあります。

replaceを何度も呼び分けると、置換結果が次の置換に影響する(連鎖)ため意図しない結果を招くことがあります。

translate変換表に基づいて各文字を独立に(同時に)置換するため、意図どおりの一括変換が可能です。

例えば以下の用途に適しています。

  • 複数の記号を一括で削除する
  • 特定の文字群(母音、記号、全角の一部など)を別の文字に正規化する
  • 簡易的な符号化(例: leetspeak のような文字の置換)

translateと変換表の仕組み

str.translateは「変換表(マッピング)」を受け取り、各文字のUnicodeコードポイントに対して、対応する置換文字列または削除(None)を適用します。

変換表は次のようなルールで解釈されます。

  • 変換表のキーは文字(長さ1の文字)またはそのord値(int)です。
  • 値は置換後の文字列(長さ1でも複数文字でも可)またはNoneです。Noneは「削除」を意味します。
  • 変換表に存在しない文字はそのまま残ります。

まずは最小限の例を見てみます。

Python
# 基本例: a,b,c を x,y,z に置換し、! と ? を削除する
text = "abc!? cab"
table = {
    ord('a'): 'x',
    ord('b'): 'y',
    ord('c'): 'z',
    ord('!'): None,
    ord('?'): None,
}

print(text.translate(table))  # 'ord'を使わず文字キーでもOK
実行結果
xyz zxy

このように、1回のtranslateで「複数の置換」と「複数の削除」を同時に実行できます。

置換と削除を同時に行う考え方

translateの考え方はシンプルで、各文字に対して「最終的にどうするか」を辞書で宣言するだけです。

削除ならNone、置換なら置換先文字列を値にします。

Python
# 置換( - と _ を空白に)と、削除( ! と ? )を同時に適用
text = "A-B_C!? 123"
table = str.maketrans({
    '-': ' ',   # 置換
    '_': ' ',   # 置換
    '!': None,  # 削除
    '?': None,  # 削除
})
print(text.translate(table))
実行結果
A B C  123

(この例では連続した空白が残る点にも注意が必要です。必要に応じて後段で空白の正規化を行います。)

str.maketransの作り方と使い方

dictで複数文字をまとめて対応付け

str.maketransは、translateに渡すマッピングを簡単に作るためのユーティリティです。

辞書で指定すると、1対多(文字→複数文字)の置換や、削除の指定がしやすくなります。

Python
# dictでマッピングを作成(1対多の置換や削除も可能)
text = "straße & œuvre!"
table = str.maketrans({
    'ß': 'ss',      # 1文字→2文字の置換
    'œ': 'oe',      # 1文字→2文字の置換
    '&': 'and',     # 1文字→3文字の置換
    '!': None,      # 削除
})
print(text.translate(table))
実行結果
strasse and oeuvre

このように辞書を使えば、柔軟な置換が可能です。

2つの文字列から1対1の対応を作る

固定長の1対1置換なら、2つの同じ長さの文字列を渡して手短に変換表を作れます。

これは「各文字を別の1文字へ」置換する用途に向いています。

Python
# 'abc' → 'XYZ' の1対1マッピング
text = "abracadabra"
table = str.maketrans('abc', 'XYZ')  # 文字列の長さは同じである必要がある
print(text.translate(table))
実行結果
XYrXZXdXYrX

注意点として、この方法は1対多(例: ‘ß’→’ss’)の置換はできません。

1対多が必要なら前節の辞書形式を使います。

削除したい文字をまとめて指定

3番目の引数に「削除したい文字すべてを列挙した文字列」を渡すと、簡潔に削除マッピングを作れます。

Python
# 数字と句読点(, !)を一括削除
text = "Hello, world! 123"
table = str.maketrans('', '', '0123456789,!')  # 第3引数が削除対象
print(text.translate(table))
実行結果
Hello world

この例では最後に空白が残っています。

空白の整理が必要なら、後処理でsplitjoin等を検討します。

translateで変換表を適用

作成した変換表は、文字列のtranslateメソッドで適用します。

複数のテキストに再利用できるため、同じ正規化処理を何度も実行するワークフローに向いています。

Python
# よく使う正規化: 長音・ダッシュ類や波ダッシュ類を統一、特定記号を削除
texts = [
    "price–list — 1,200円",
    "range ~ 10〜20",
    "dash ― emphasis",
]

# 異体のダッシュや波ダッシュを統一し、カンマは削除
table = str.maketrans({
    '–': '-', '—': '-', '―': '-',  # ダッシュ系をハイフンに統一
    '~': '~', '〜': '~',           # 波ダッシュ系をASCIIに統一
    ',': None,                      # カンマ削除
})

for s in texts:
    print(s.translate(table))
実行結果
price-list - 1200円
range ~ 10~20
dash - emphasis

必要な範囲に限定して正規化を行うと、副作用を抑えやすくなります。

str.replaceとの違いと使い分け

replaceは単一パターンを逐次置換

str.replace(old, new)は「1つのパターン」を別の文字列に置換します。

複数の置換を連続で行うと、先の置換結果が次に影響するため、意図しない変換をしてしまうことがあります。

Python
# 連鎖するreplaceの例(結果が次の置換に影響)
s = "abc"
result = s.replace('a', 'b').replace('b', 'c').replace('c', 'a')
print(result)
実行結果
aaa

本来「a→b、b→c、c→a」を同時に適用したい場合、replaceの連鎖は適していません。

translateは複数文字を同時に一括置換

translateはマッピングに基づいて各文字へ同時適用するため、上記のような相互変換も意図通りに動きます。

Python
# translateなら同時適用される
s = "abc"
table = str.maketrans({'a': 'b', 'b': 'c', 'c': 'a'})
print(s.translate(table))
実行結果
bca

この違いが、translateを使う大きな理由のひとつです。

パフォーマンスとメモリの違い

translateは内部で1パス処理しながらマッピングを参照するため、多数の1文字置換・削除をまとめて行う場合に高速です。

一方、replaceを複数回呼ぶと、そのたびに新しい文字列が生成されるため、メモリ割り当てとコピーが繰り返され、相対的に遅くなりやすいです。

簡単なベンチマーク例を示します。

Python
# 簡易ベンチマーク(実行環境や条件で結果は変わります)
import timeit

text = ("The quick brown fox jumps over the lazy dog!? 12345\n" * 5000)

# translate: 母音を大文字化、!?を削除、数字を削除
table = str.maketrans({
    'a': 'A', 'e': 'E', 'i': 'I', 'o': 'O', 'u': 'U',
    '!': None, '?': None,
    '0': None, '1': None, '2': None, '3': None, '4': None,
    '5': None, '6': None, '7': None, '8': None, '9': None,
})

def via_translate():
    return text.translate(table)

def via_replace():
    s = text
    for old, new in [('a','A'),('e','E'),('i','I'),('o','O'),('u','U')]:
        s = s.replace(old, new)
    for ch in '!?0123456789':
        s = s.replace(ch, '')
    return s

print("translate:", timeit.timeit(via_translate, number=10))
print("replace chain:", timeit.timeit(via_replace, number=10))
実行結果
translate: 0.025
replace chain: 0.091

多くの小さな置換・削除をまとめたいとき、translateは実務で体感差が出やすいです。

以下は機能面の比較表です。

観点str.replacestr.translate
パターン単位文字列(1文字以上)基本は1文字単位
同時適用できない(逐次)できる(同時)
複数文字の削除1回につき1パターン複数文字を一括で削除可能(None/第3引数)
1→多の置換可能dictマッピングなら可能(文字→複数文字)
文字以外のパターン可能(部分文字列)不可(基本は1文字)
パフォーマンス連鎖すると遅くなりやすい多数の一括置換・削除に強い

使い分けの判断基準

  • 1つの固定フレーズを置換するだけならreplaceが簡潔です。
  • 複数の1文字置換・削除をまとめて行う、または「同時適用」が必要な場合はtranslateが安全かつ高速です。
  • 1文字を複数文字に展開したい(例: ‘ß’→’ss’)なら、translateに辞書マッピングを渡します。
  • 1文字以上の部分文字列を複雑に置換する必要があるなら、replaceや正規表現(re.sub)を検討します。

よくある落とし穴とベストプラクティス

1対多の置換はdictを使う

str.maketrans('abc','xyz')のような2文字列指定は1対1のみです。

1対多(1文字→複数文字)は辞書で定義します。

Python
# 1対多置換の例
text = "maß 5㎏"  # '㎏'(U+338F)は互換文字
table = str.maketrans({
    'ß': 'ss',
    '㎏': 'kg',  # 互換用の単位文字をASCIIに
})
print(text.translate(table))
実行結果
mass 5kg

変換表にない文字はそのまま残る

マッピングに存在しない文字は変更されません。

これは「影響範囲を限定した正規化」がしやすい、という利点にもなります。

Python
text = "abcde"
table = str.maketrans({'a': 'A', 'e': 'E'})
print(text.translate(table))  # b, c, d はそのまま
実行結果
AbcdE

大文字小文字や全角半角の注意点

translateは指定した文字にだけ作用します。

大文字小文字を両方扱うなら両方のキーを用意します。

また全角半角の正規化を網羅的に行いたい場合、unicodedata.normalize('NFKC', s)の活用も検討してください。

translateと組み合わせると強力です。

Python
# 大文字小文字を揃えつつ特定記号を削除
import unicodedata

text = "AbC!? abc"
# まず互換正規化で全角→半角へ
normalized = unicodedata.normalize('NFKC', text)
# 次に母音を大文字化し、!?を削除
table = str.maketrans({'a':'A','e':'E','i':'I','o':'O','u':'U','!':None,'?':None})
print(normalized.translate(table))
実行結果
AbC AbC

必要に応じて、正規化の有無や順序を調整します。

連鎖replaceで結果が変わる問題

相互変換や衝突する置換規則があると、replaceの連鎖は結果が崩れます。

translateは同時適用なので、こうした問題を避けられます。

Python
# 'a'↔'b' の入れ替えで比較
s = "abba"
rep = s.replace('a', 'b').replace('b', 'a')  # 逐次: 先の結果に影響
trans = s.translate(str.maketrans({'a':'b','b':'a'}))  # 同時

print(rep)
print(trans)
実行結果
aaaa
baab

変換表の再利用で処理を高速化

同じ変換を繰り返し適用する場合は、変換表を一度だけ作って再利用します。

ループ内で毎回str.maketransを作ると無駄が増えます。

Python
# モジュールトップや初期化時に一度だけ用意
NORMALIZE_TABLE = str.maketrans({
    '–': '-', '—': '-', '―': '-',
    '~': '~', '〜': '~',
    ',': None, '!': None, '?': None,
})

def normalize_line(s: str) -> str:
    # 何度呼んでもテーブルは再利用される
    return s.translate(NORMALIZE_TABLE)

# 例: 多数行の前処理
lines = ["price–list—2024,!?","range 10〜20","ok"]
print([normalize_line(x) for x in lines])
実行結果
['price-list-2024', 'range 10~20', 'ok']

大規模データの前処理で効果が大きく、速度とメモリ効率の改善が見込めます。

まとめ

str.translateは「複数の1文字置換・削除を同時に行う」用途に理想的で、正確さと速度の両面で有利です。

変換表はstr.maketransで簡潔に作成でき、辞書を使えば1対多の置換や多数の削除をまとめて表現できます。

一方、str.replaceは部分文字列を対象とした単発の置換に向いており、複雑な同時変換には不向きです。

実務では、置換の性質(同時か逐次か、1文字か部分文字列か)とパフォーマンス要件を見極め、以下の方針で使い分けるのがおすすめです。

  • 多数の1文字置換・削除、相互変換や同時適用が必要ならtranslateを使う
  • 1つの固定パターンの置換ならreplaceで簡潔に
  • 1対多の置換はtranslateに辞書マッピングを渡す
  • 全角半角や互換文字の正規化が必要なら、unicodedata.normalizeと組み合わせる
  • 変換表は一度だけ作って再利用し、処理を高速化する

これらを押さえることで、安全で読みやすく、高速な文字列処理が実現できます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!