rstrip()方法作用
删除字符串末尾的指定字符,默认空白符,包括空格、换行符、回车符、制表符
s = "hello\n \t \r "
s.rstrip() # hello
a = "python.py"
a.rstrip('.py') # python
看着用法很简单,直到有一天偶然发现这样一段代码
a = "pythony.py"
a.rstrip('.py') # python
按照我之前的理解,应该输出pythony,为啥是python呢?赶紧看官方文档
str.rstrip([chars]) 返回原字符串的副本,移除其中的末尾字符。 chars 参数为指定要移除字符的字符串。 如果省略或为
None,则 chars 参数默认移除空白符。 实际上 chars 参数并非指定单个后缀;而是会移除参数值的所有组合:
似乎明白了一些,想要理解的更透彻,就得翻翻源码了。
#define LEFTSTRIP 0
#define RIGHTSTRIP 1
#define BOTHSTRIP 2
static PyObject *
unicode_rstrip_impl(PyObject *self, PyObject *chars)
/*[clinic end generated code: output=4a59230017cc3b7a input=62566c627916557f]*/
{
return do_argstrip(self, RIGHTSTRIP, chars);
}
static PyObject *
do_argstrip(PyObject *self, int striptype, PyObject *sep)
{
if (sep != Py_None) {
if (PyUnicode_Check(sep))
return _PyUnicode_XStrip(self, striptype, sep);
else {
PyErr_Format(PyExc_TypeError,
"%s arg must be None or str",
STRIPNAME(striptype));
return NULL;
}
}
return do_strip(self, striptype);
}
可以看到,如果指定字符不为空,会调用PyUnicode_Check函数检查字符串类型,如果是则调用_PyUnicode_XStrip,否则抛出异常。
a = "pythony.py"
a.rstrip(1) # TypeError: rstrip arg must be None or str
我们看看_PyUnicode_XStrip函数
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;
if (PyUnicode_READY(self) == -1 || PyUnicode_READY(sepobj) == -1)
return NULL;
kind = PyUnicode_KIND(self);
data = PyUnicode_DATA(self);
len = PyUnicode_GET_LENGTH(self);
PyObject_Print(PyLong_FromSsize_t(len), stdout, 0); // 调用api输出,自己调试加的
seplen = PyUnicode_GET_LENGTH(sepobj);
sepmask = make_bloom_mask(PyUnicode_KIND(sepobj),
PyUnicode_DATA(sepobj),
seplen);
i = 0;
if (striptype != RIGHTSTRIP) {
while (i < len) {
Py_UCS4 ch = PyUnicode_READ(kind, data, i);
if (!BLOOM(sepmask, ch))
break;
if (PyUnicode_FindChar(sepobj, ch, 0, seplen, 1) < 0)
break;
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);
}
解释一下这段代码
- 首先检查输入的self和sepobj是否已经准备好。如果任意一个对象没有准备好,则返回NULL
- 获取self对象的数据和长度,以及sepobj对象的数据和长度。同时,生成一个Bloom filter的掩码,用于快速判断一个字符是否属于sepobj中的字符集
- 根据striptype的值,决定是去除左边、右边还是两边的字符。如果是去除左边的字符,那么从字符串的左端开始扫描,找到第一个不属于sepobj的字符的位置i,然后返回从i到字符串结尾的子串。如果是去除右边的字符,那么从字符串的右端开始扫描,找到第一个不属于sepobj的字符的位置j,然后返回从字符串开头到j的子串。如果是去除两边的字符,那么先执行左边的去除操作,然后再在结果的基础上执行右边的去除操作。
- 返回去除前缀和/或后缀后的新字符串对象
- 这段代码使用了Bloom filter来加速字符集的匹配操作,从而提高了函数的执行效率。Bloom filter是一种基于哈希的数据结构,可以用来判断一个元素是否属于一个集合,具有快速、高效的特点。在这段代码中,我们将sepobj中的所有字符都插入到了一个Bloom filter中,然后在扫描字符串时,只需要通过Bloom filter来快速判断一个字符是否属于sepobj中的字符集,从而避免了多次调用PyUnicode_FindChar函数的开销。这种优化技巧在处理大量数据时非常有效,可以大大提高程序的性能。
结合案例
a = "pythony.py"
a.rstrip('.py')
j = 9, ch = 'y',依次判断ch是否在'.py'中可以找到,j = 5,ch = 'n', 此时在'.py'中未找到,j++变为6,最终返回,类似切片a[0:6],所以输出'python'
到目前为止,我们已经了解了rstrip()底层的运行逻辑,那我们就想要删除指定字符串,像上面的案例,只需删除'.py',最终输出'pythony'呢?
removesuffix()
对,这个方法就可以解决上面的问题。不过这是python3.9版本的新功能
官方介绍
str.removesuffix(suffix)
如果字符串以 suffix 字符串结尾,并且 suffix 非空,返回
string[:-len(suffix)]。 否则,返回原始字符串的副本
看看源码
static PyObject *
unicode_removesuffix_impl(PyObject *self, PyObject *suffix)
/*[clinic end generated code: output=d36629e227636822 input=12cc32561e769be4]*/
{
int match = tailmatch(self, suffix, 0, PY_SSIZE_T_MAX, +1);
if (match == -1) {
return NULL;
}
if (match) {
return PyUnicode_Substring(self, 0, PyUnicode_GET_LENGTH(self)
- PyUnicode_GET_LENGTH(suffix));
}
return unicode_result_unchanged(self);
}
就是一个字符串的截取操作
欢迎关注公众号“郝同学的测开笔记”