Remove final characters from string recursively - What's the best way to do this?
<p>I am reading lines from a file one at a time and before i store each, i wanna modify them according to the following simple rule:</p>

<ul>
<li>if the last character is not any of, e.g., <code>{'a', 'b', 'c'}</code> store the line.</li>
<li>if that is not the case, remove the character (pop-like) and check again.</li>
</ul>

<p>What i currently have (felt like the obvious thing to do) is this:</p>

<pre><code>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 -&gt; jkhasdkjashdasjkd|
</code></pre>

<p>Which of course works, but the string slicing\reconstruction seems a bit too excessive to my untrained eyes. So i was wondering a couple of things:</p>

<ol>
<li>is there a better way to do this? like a <code>pop</code> type of function for strings?</li>
<li>how is <code>rstrip()</code> or <code>strip()</code> in general implemented? is it also with a <strong>while</strong>?</li>
<li>would it be worthwhile making <code>rstrip()</code> recursive for this example?</li>
<li>Finally, how much better is the following:</li>
</ol>

<hr>

<pre><code>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]
</code></pre>

<p>Any comment on any of the points made above would be appreciated âº.</p>

<p><strong>Note: the separator ("|") is only there for visualization.</strong></p>

<p>Slicing creates lots of unnecessary temporary copies of string. Recursion would be even worse - copies would still be made, and on top of it function call overhead would be intruduced. Both approaches are not that great.</p>

<p>You may find an <code>rstrip</code> implementation in <a href="https://github.com/python/cpython/blob/master/Objects/bytesobject.c#L2030" rel="nofollow">CPython source code</a>. An iterative approach (similar to your last code snippet) is used there.</p>

<pre><code>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, &amp;vsep, PyBUF_SIMPLE) != 0)
        return NULL;
    sep = vsep.buf;
    seplen = vsep.len;

    i = 0;
    if (striptype != RIGHTSTRIP) {
        while (i &lt; len &amp;&amp; memchr(sep, Py_CHARMASK(s[i]), seplen)) {
            i++;
        }
    }

    j = len;
    if (striptype != LEFTSTRIP) {
        do {
            j--;
        } while (j &gt;= i &amp;&amp; memchr(sep, Py_CHARMASK(s[j]), seplen));
        j++;
    }

    PyBuffer_Release(&amp;vsep);

    if (i == 0 &amp;&amp; j == len &amp;&amp; PyBytes_CheckExact(self)) {
        Py_INCREF(self);
        return (PyObject*)self;
    }
    else
        return PyBytes_FromStringAndSize(s+i, j-i);
}
</code></pre>

<p>So to sum up, your intuition to use index-based parsing is correct. Main advantage is that no temporary strings are created and copying things in memory is significantly reduced.</p>

python