[python]strip()を使う前に知っておきたい仕様と落とし穴

記事内に広告が含まれています。

実務でつまづいたので、戒めも込めてメモ。

結論

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);
}

Python
スポンサーリンク