複数の文字を一括で置換・削除したいとき、Pythonのstr.translateは非常に強力です。
1回の走査で多くの文字を同時に扱えるため、順序に依存せず高速に正規化ができます。
本記事ではstr.maketransで作る置換マップの基本から、replaceやre.subとの使い分け、実用的な例まで初心者向けに丁寧に解説します。
- 関連記事:replaceで文字列を置換する
- 関連記事:正規表現(re)入門
str.translateの基本(複数文字を一括置換)
複数の文字を同時に置換・削除できる
同時置換のイメージ
str.translateは「文字ごと」にマッピング表(変換テーブル)を参照して、各文字を置換または削除します。
トランスレーションテーブルはstr.maketransで作ります。
基本的な呼び出し方
次の例では、Aを@、Bを8に置換し、ハイフン-は削除します。
# 基本: 同時に複数置換+削除
s = "ABC-123-XYZ"
# dictで置換マップを記述。削除は None を指定します。
table = str.maketrans({
"A": "@",
"B": "8",
"-": None, # ハイフンは削除
})
result = s.translate(table)
print(result)
@8C123XYZ
ポイントとして、1回の処理で全部の文字に対して同時に適用されるので、先にAを置換してからB…という順序を考える必要がありません。
置換は1文字単位で部分文字列は不可
部分文字列(長さ2以上)はキーにできない
注意として、translateは「1文字単位」の置換しかできません。
例えば"th"を一度に"X"へ置換する、といった「部分文字列」置換は不可です。
# 部分文字列の置換を試みる(誤りの例)
try:
table = str.maketrans({"th": "X"}) # キーが2文字なのでNG
except Exception as e:
print(type(e).__name__, ":", e)
ValueError : dictionary keys must be strings of length 1 or integers
部分文字列の置換をしたい場合はstr.replaceやre.subを使います。
Python初心者向けの使いどころ
正規化やサニタイズに強い
全角記号を半角へ揃える、スマートクォートを通常の引用符へ統一、複数種類のダッシュをハイフンへ統一など、文字ごとの正規化に向いています。
また削除文字を一括指定できるため、入力から不要文字をまとめて除去する用途にも適しています。
maketransの作り方と置換マップ
文字列2本(from,to)でマッピングを作成
等長の2本の文字列で1対1マッピング
str.maketrans(from_str, to_str)は、同じ長さの2つの文字列を受け取り、対応する位置の文字どうしで置換します。
# "abc" を "ABC" へ対応させる
table = str.maketrans("abc", "ABC")
print("abracadabra".translate(table))
ABrACAdABrA
長さが一致しない場合はValueErrorになります。
- 関連記事:文字列と数値の型を変換したい
- 関連記事:Pythonで文字数をカウントはlen()を使う
dictでUnicodeコードポイントを指定
ordで細かく制御
辞書でord(文字)をキーにすると、Unicodeコードポイント単位で指定できます。
これにより、特殊文字(丸数字など)の正規化がしやすくなります。
# 丸数字や記号を通常の数字・記号へ
table = str.maketrans({
ord("①"): "1",
ord("②"): "2",
ord("③"): "3",
ord("—"): "-", # エムダッシュをハイフンへ
})
print("得点①—③を選択".translate(table))
得点1-3を選択
第3引数で削除文字を指定(delete)
まとめて消すなら第3引数
str.maketrans(from_str, to_str, delete_str)の第3引数delete_strに含む文字は削除されます。
置換と削除を同時に仕込めます。
# 数字を全角→半角に、かつカンマと空白は削除
table = str.maketrans("0123456789", "0123456789", ", ")
print("1,2 3".translate(table))
123
同じ「削除」は辞書で文字: Noneと書いてもOKです。
1文字を複数文字に展開する
1→Nの展開が可能
1文字を複数文字へ置換できます。
例えばドイツ語のßをssに展開します。
table = str.maketrans({"ß": "ss", "æ": "ae"})
print("straße • encyclopædia".translate(table))
strasse • encyclopaedia
replaceとの違いと使い分け
順序に依存しない同時置換が可能
置換順序の罠を回避
translateは同時置換なので、入れ替え(スワップ)でも安全です。
replaceの連続適用は順序に依存して壊れることがあります。
s = "ABABA"
# replaceの連鎖(順序依存)
r1 = s.replace("A", "B").replace("B", "A")
# translateは同時置換なので意図通りに入れ替わる
table = str.maketrans({"A": "B", "B": "A"})
r2 = s.translate(table)
print("replace:", r1)
print("translate:", r2)
replace: AAAAA
translate: BABAB
回数指定(count)はreplaceのみ
一部だけ置換したいならreplace
replaceにはcount引数があり、最初のn回だけ置換といった制御が可能です。
translateには回数指定がありません。
s = "spam spam spam"
print(s.replace("spam", "eggs", 1)) # 最初の1回だけ置換
eggs spam spam
- 関連記事:スライスで文字列の一部を取得する
大量置換でのパフォーマンスはtranslateが有利
1パスで高速
多種類の文字を一括で処理するならtranslateが有利です。
以下は簡易ベンチマーク例です(結果は環境により変化します)。
import timeit
text = "A-B_C:D;E " * 10000
table = str.maketrans("", "", "-_:; ")
def via_translate():
return text.translate(table)
def via_replace_chain():
return text.replace("-", "").replace("_", "").replace(":", "").replace(";", "").replace(" ", "")
# ベンチマーク
t1 = timeit.timeit(via_translate, number=50)
t2 = timeit.timeit(via_replace_chain, number=50)
print("translate :", t1)
print("replace chain:", t2)
translate : 0.0015
replace chain: 0.016
str.maketrans("", "", "-_:; ") のようにすると、一気に複数文字を削除できる。
一般にtranslateは1回の走査で完結するのに対し、置換を何度も繰り返すと文字列の再生成コストが嵩みます。
正規表現が必要ならre.subとの使い分け
パターン一致はre.sub
translateは部分文字列やパターンを扱えません。
繰り返し空白を1つに縮約するような場合はre.subが適します。
import re
s = "a b\t\tc\n d"
print(re.sub(r"\s+", " ", s).strip())
a b c d
まとめると、単純な文字ごとの正規化や大量削除→translate、部分文字列や回数制御→replace、パターン→re.subという住み分けになります。
- 関連記事:文字列が数字/英字/空白か判定する
- 関連記事:正規表現(re)入門
以下に要点の比較表を示します。
| 特徴 | translate | replace | re.sub |
|---|---|---|---|
| 同時置換(順序非依存) | 強い | 弱い(順序依存) | 可(工夫次第) |
| 回数指定 | なし | あり | あり(パターンに依存) |
| 大量の文字種に対する性能 | 有利 | 連鎖で不利 | パターンによりけり |
| パターン表現 | 不可 | 不可 | 可能 |
例で学ぶstr.translate
記号を一括置換して正規化する例
スマートクォートや長音・ダッシュ類の統一
テキスト収集時に混ざりがちな記号をASCIIへ統一します。
text = '“Python”—それは「汎用」言語。価格は1,234円。メールはfoo—bar@example.com'
# 代表的なUnicode記号をASCIIへ正規化
table = str.maketrans({
"“": '"', "”": '"',
"‘": "'", "’": "'",
"—": "-", "–": "-", "−": "-", # ダッシュ/マイナスもハイフンへ
"〜": "~", "~": "~",
":": ":", ";": ";",
"(": "(", ")": ")",
"[": "[", "]": "]",
"{": "{", "}": "}",
",": ",", "。": ".",
"1": "1", "2": "2", "3": "3", "4": "4",
})
normalized = text.translate(table)
print(normalized)
"Python"-それは「汎用」言語.価格は1,234円.メールはfoo-bar@example.com
多様な記号を一度に正規化できるのが大きな利点です。
必要に応じてマップを足していけます。
数字以外を削除する例
入力に含まれる非数字だけを狙って消す
全Unicodeから非数字を列挙するのは現実的ではないため、対象文字列に現れた非数字のみを削除指定する方法が簡便です。
s = "注文番号: No.AB-123 456-xyz"
# 文字列中に現れる、数字以外の文字を集めて削除指定
delete_chars = "".join({ch for ch in s if not ch.isdigit()})
table = str.maketrans("", "", delete_chars)
digits_only = s.translate(table)
print(digits_only)
123456
入力ごとに削除集合を作るため、過剰削除を避けやすいのが利点です。
固定の入力規格がある場合は、あらかじめ許容文字以外を列挙して削除するマップを作るのも有効です。
複数の空白や記号をまとめて削除する例
ホワイトスペースと句読点を一括除去
stringモジュールの定義済み集合を活用すると便利です。
import string
s = " \tHello,-_ World!! \nNext\tLine... "
# 空白類(string.whitespace)と句読点(string.punctuation)を削除
delete = string.whitespace + string.punctuation
table = str.maketrans("", "", delete)
cleaned = s.translate(table)
print(cleaned)
HelloWorldNextLine
translateなら「削りたい文字の種類」を並べるだけで、1回の処理で一気にきれいにできます。
まとめ
str.translateは「文字ごと」の同時置換・削除を高速に行える標準機能です。
str.maketransで置換マップを作り、記号・全角半角・特殊文字の正規化や不要文字の除去を1パスで実現できます。
部分文字列の置換は不可という制約はありますが、そこはreplaceやre.subと適材適所で使い分ければ問題ありません。
実運用では、頻出の正規化ルールをマップとして蓄積し、順序に依存しない同時置換と高速性を活かして、堅牢な前処理パイプラインを構築していきましょう。
