実務でつまづいたので、戒めも込めてメモ。
結論
strip()の引数に文字列を指定した場合、その文字列が両端から削除できるが、以下の挙動で削除処理が実行されることに注意。
- ❌ 完全一致
- ✅ 引数に含まれる文字が削除される
以下のコードの挙動の正しい理解は、「両端から ‘a’ もしくは ‘b’ を消し続ける」になる。
- ❌ ’ab’ という文字列を両端から削除する
- ✅ ’a’ もしくは ‘b’ を両端から削除する
# stripの例
s = 'abcab'
strip_s = s.strip('ab')
print(strip_s) # c
‘ab’という文字列の1セットとして削除したい場合、 removeprefix, removesuffixを使った方が安全。
s = 'abcab'
s = s.removeprefix('ab') #接頭辞のみ
print(s) # cab
s = s.removesuffix('ab') #接尾辞のみ
print(s) # c実務で起きた事故
特定の接頭辞・接尾辞をもつ文字列から、それらを削除した値を抽出するロジックにstrip()を使ったことで、抽出しようとした文字列まで削除される事象が発生してしまった。
test1 = 'aaHello!zz' # aaが接頭辞、zzが接尾辞。欲しいのは Hello!
strip_test1 = test1.strip('aa').strip('zz') # 期待値は Hello!
print(strip_test1) # Hello! 期待値通り
# 事故の再現
test2 = 'aaabczz' # aaが接頭辞、zzが接尾辞、欲しいのは abc
trip_test2 = test2.strip('aa').strip('zz') # 期待値は abc
print(strip_test2) # bc 期待値と違う!strip()関数のソースコードを見てみる
ソースコードのイメージとしては、両端の文字が削除対象であるごとに左/右にずらす操作をしている。
# strip()関数のイメージ
while(左端の文字が 削除対象)
l++;
while(右端の文字が削除対象)
r--;
return 文字列[l:r] 以下、github のソースより抜粋
cpython/Objects/unicodeobject.c at main · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
/* externally visible for str.strip(unicode) */
PyObject *
_PyUnicode_XStrip(PyObject *self, int striptype, PyObject *sepobj)
{
const void *data;
int kind;
Py_ssize_t i, j, len;
BLOOM_MASK sepmask;
Py_ssize_t seplen;
kind = PyUnicode_KIND(self);
data = PyUnicode_DATA(self);
len = PyUnicode_GET_LENGTH(self);
seplen = PyUnicode_GET_LENGTH(sepobj);
# strip()で指定した引数のマスク
sepmask = make_bloom_mask(PyUnicode_KIND(sepobj),
PyUnicode_DATA(sepobj),
seplen);
i = 0;
// 右だけ削除の指定でない場合=先頭削除処理
if (striptype != RIGHTSTRIP) {
while (i < len) {
# 文字列オブジェクトのi番目の文字
Py_UCS4 ch = PyUnicode_READ(kind, data, i);
# i番目の文字が削除対象でなければスキップ
if (!BLOOM(sepmask, ch))
break;
if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0)
break;
#i番目の文字が削除対象だった場合
i++;
}
}
j = len;
if (striptype != LEFTSTRIP) {
j--;
while (j >= i) {
Py_UCS4 ch = PyUnicode_READ(kind, data, j);
if (!BLOOM(sepmask, ch))
break;
if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0)
break;
j--;
}
j++;
}
# 削除対象だった文字を両端から除いて返す
return PyUnicode_Substring(self, i, j);
}
