我正在逐个读取文件中的行,在我存储每行之前,我想根据以下简单规则修改它们:
{'a', 'b', 'c'}
存储该行。我目前所拥有的(感觉就像显而易见的事情)是:
bad_chars = {'a', 'b', 'c'}
def remove_end_del(line_string, chars_to_remove):
while any(line_string[-1] == x for x in chars_to_remove):
line_string = line_string[:-1]
return line_string
example_line = 'jkhasdkjashdasjkd|abbbabbababcbccc'
modified_line = remove_end_del(example_line, bad_chars)
print(modified_line) # prints -> jkhasdkjashdasjkd|
这当然有效,但字符串切片\重建对我未经训练的眼睛来说似乎有些过分。所以我想知道几件事:
pop
类型的字符串函数?rstrip()
或strip()
一般如何实施?是否还有而?rstrip()
递归?def remove_end_del_2(line_string, chars_to_remove):
i = 1
while line_string[-i] in chars_to_remove:
i += 1
return line_string[:-i+1]
对上述任何一点的任何评论都将受到赞赏<。
注意:分隔符(&#34; |&#34;)仅用于可视化。
答案 0 :(得分:4)
re.sub
的另一种近乎快速的方法虽然更直观(听起来像是pop
你要求的),但是itertools.dropwhile
:
创建一个迭代器,只要在迭代器中删除元素 谓词是真的;
>>> ''.join(dropwhile(lambda x: x in bad_chars, example_line[::-1]))[::-1]
'jkhasdkjashdasjkd|'
然而,看起来rstrip
已经制作出来并且更适合这项任务。
一些时间:
In [4]: example_line = 'jkhasdkjashdasjkd|abbbabbababcbccc'
In [5]: bad_chars = {'a', 'b', 'c'}
In [6]: %%timeit
...: re.sub(r'[%s]+$' % ''.join(bad_chars), '', example_line)
...:
100000 loops, best of 3: 5.24 µs per loop
In [7]: %%timeit
...: ''.join(dropwhile(lambda x: x in bad_chars, example_line[::-1]))[::-1]
...:
100000 loops, best of 3: 5.72 µs per loop
In [10]: %%timeit
....: remove_end_del(example_line, bad_chars)
....:
10000 loops, best of 3: 24.1 µs per loop
In [11]: %%timeit
....: example_line.rstrip('abc')
....:
1000000 loops, best of 3: 579 ns per loop
In [14]: %%timeit
....: remove_end_del_2(example_line, bad_chars)
....:
100000 loops, best of 3: 4.22 µs per loop
rstrip
获胜!
答案 1 :(得分:2)
不是问题的直接答案,但另一种选择是使用正则表达式删除字符串末尾的错误字符:
>>> import re
>>>
>>> example_line = 'jkhasdkjashdasjkd|abbbabbababcbccc'
>>> bad_chars = {'a', 'b', 'c'}
>>>
>>> re.sub(r'[%s]+$' % ''.join(bad_chars), '', example_line)
'jkhasdkjashdasjkd|'
这里的正则表达式是从“坏”字符集动态构造的。在这种情况下,它会(或“可能”,因为集合没有顺序)为[abc]+$
:
[abc]
定义“字符类” - 任何“a”,“b”或“c”都匹配+
表示1个或更多$
定义字符串的结尾(请注意,如果“坏”字符可以包含可能在字符类中具有特殊含义的字符(例如,[
或]
),则应将其转义为re.escape()
)。
最后一句话可能证明old saying关于比最初有更多问题。
答案 2 :(得分:2)
切片会创建大量不必要的字符串临时副本。递归会更糟糕 - 仍然会产生副本,并且在它之上会引入函数调用开销。这两种方法都不是很好。
您可以在CPython source code中找到rstrip
实施。在那里使用迭代方法(类似于您的上一个代码片段)。
Py_LOCAL_INLINE(PyObject *)
do_xstrip(PyBytesObject *self, int striptype, PyObject *sepobj)
{
Py_buffer vsep;
char *s = PyBytes_AS_STRING(self);
Py_ssize_t len = PyBytes_GET_SIZE(self);
char *sep;
Py_ssize_t seplen;
Py_ssize_t i, j;
if (PyObject_GetBuffer(sepobj, &vsep, PyBUF_SIMPLE) != 0)
return NULL;
sep = vsep.buf;
seplen = vsep.len;
i = 0;
if (striptype != RIGHTSTRIP) {
while (i < len && memchr(sep, Py_CHARMASK(s[i]), seplen)) {
i++;
}
}
j = len;
if (striptype != LEFTSTRIP) {
do {
j--;
} while (j >= i && memchr(sep, Py_CHARMASK(s[j]), seplen));
j++;
}
PyBuffer_Release(&vsep);
if (i == 0 && j == len && PyBytes_CheckExact(self)) {
Py_INCREF(self);
return (PyObject*)self;
}
else
return PyBytes_FromStringAndSize(s+i, j-i);
}
总而言之,您使用基于索引的解析的直觉是正确的。主要优点是不会创建临时字符串,并且会大大减少在内存中复制内容。
答案 3 :(得分:1)
我理解过度的含义,但我认为一般来说,它看起来不错。另一种方法是使用索引,这是不可读的。 (我也碰巧认为正则表达式也不是很易读......)
但是,如果您有memoryview
对象可能相关或不相关,则可以使用bytes
:
https://docs.python.org/3/library/stdtypes.html#memoryview
<强> 1。字符串的pop
函数
.pop
没有str
方法。您必须使用list(line_string).pop()
,其中list(s)
创建一个列表,其中字符串的每个字符都作为元素。
<强> 2。 (r)strip
实施
这可能是暂时实现的,是的。 它应该是所有C代码。
第3。递归rstrip
首先,为什么你需要让它递归? 其次,我认为(递归)会使心理负担不必要地高 - 所以,不。
<强> 4。最后,以下内容有多好:
测量它!肯定会更快。