その文字列処理、import reしなくても標準ライブラリだけで書けます
「前後の文字列を切り出したい」「特定の接頭辞を取り除きたい」となると、反射的に import re して正規表現を書き始めていませんか?
実は簡単な文字列操作であれば、Python標準の str メソッドだけで、正規表現より読みやすく書けることが多くあります。
接頭辞・接尾辞の削除にはremoveprefix/removesuffix
ファイル名から拡張子だけ取りたい、URLからプロトコル部分だけ取りたい、というときによく見るのが以下のような正規表現です。
import re
url = "https://example.com"
domain = re.sub(r"^https://", "", url)
これは str.removeprefix() を使うだけで、正規表現なしで書けます。
url = "https://example.com"
domain = url.removeprefix("https://")
print(domain)
# example.com
同様に、末尾を取り除きたい場合は str.removesuffix() を使います。
filename = "report.txt"
name = filename.removesuffix(".txt")
print(name)
# report
removeprefix() と removesuffix() はPython 3.9で追加されたメソッドです(PEP 616)。Python 3.8以前の環境で動かす必要があるコードでは使えないため、startswith() と文字列スライスを組み合わせた書き方に置き換える必要があります。
似たメソッドに strip() がありますが、こちらは「指定した文字の集合」を端から削っていく仕様のため、複数文字からなる接頭辞・接尾辞の削除には向きません。
たとえば "xxprefix".strip("x") のように、意図せず余分な文字まで削れてしまうことがあります。
ピンポイントに1つの文字列を取り除きたい場合は、removeprefix() / removesuffix() のほうが安全です。
区切り文字での分割にはpartition
「最初に出てきた = の前後で分割したい」というケースで、re.split を使っている例もよく見かけます。
import re
line = "name=Alice=Bob"
key, value = re.split(r"=", line, maxsplit=1)
これは str.partition() で置き換えられます。
line = "name=Alice=Bob"
key, sep, value = line.partition("=")
print(key) # name
print(value) # Alice=Bob
partition() は「区切り文字より前」「区切り文字そのもの」「区切り文字より後ろ」の3つをタプルで返します。
区切り文字が見つからなかった場合でも例外にはならず、(元の文字列, "", "") が返るため、try/except で囲む必要がないのも扱いやすい点です。
末尾から分割したい場合は rpartition() を使うと、最後に出てきた区切り文字を基準に分割できます。
path = "/var/log/app/error.log"
_, _, filename = path.rpartition("/")
print(filename)
# error.log
ワイルドカードでのパターンマッチにはfnmatch
*.log のような、シェルでおなじみのワイルドカード形式でファイル名を判定したいだけなのに、わざわざ正規表現に変換して書いているケースもあります。
import re
pattern = re.compile(r".*\.log$")
if pattern.match("error.log"):
print("matched")
このような単純なワイルドカードでの判定は、標準ライブラリの fnmatch モジュールで十分です。
import fnmatch
if fnmatch.fnmatch("error.log", "*.log"):
print("matched")
fnmatch.fnmatch() は *(任意の文字列)や ?(任意の1文字)といった、シェルのワイルドカードと同じ記法をそのまま使えます。
正規表現の特殊文字(. や \ など)を意識する必要がない分、ファイル名のパターンマッチのような用途では読みやすさが上がります。
使い分けの目安
すべてを標準の文字列メソッドに寄せるべきというわけではなく、向き不向きがあります。
| やりたいこと | 向いている方法 |
|---|---|
| 決まった接頭辞・接尾辞の削除 |
removeprefix() / removesuffix()
|
| 1つの区切り文字での分割 |
partition() / rpartition()
|
| ワイルドカード形式でのファイル名判定 | fnmatch |
| 桁数や文字種を条件にした複雑なパターン抽出 | re |
| 複数のパターンを組み合わせた判定 | re |
「見つけたい文字列の形」が具体的に1つに決まっているなら標準メソッドで書けることが多く、「数字が2〜4桁」「英数字とハイフンの組み合わせ」のように条件が抽象的になるほど、正規表現の出番になってくるという感覚で使い分けると判断しやすいです。
まとめ
- 接頭辞・接尾辞の削除は
removeprefix()/removesuffix()(Python 3.9以降)で正規表現なしに書ける - 1つの区切り文字での分割は
partition()/rpartition()が例外を気にせず使えて扱いやすい - ワイルドカード形式のパターンマッチは
fnmatchモジュールで十分なことが多い - パターンが具体的なら標準メソッド、条件が抽象的になってきたら正規表現、という基準で使い分ける
正規表現が悪いわけではありませんが、単純な処理にまで持ち出すと、あとから読む人にとって「なぜここまで複雑な書き方をしているのか」と余計な負担をかけてしまいます。
次に文字列処理を書くときは、一度標準メソッドで足りないか確認してみてください。