mirror of
https://github.com/python/cpython.git
synced 2025-07-23 11:15:24 +00:00
SF bug # 493951 string.{starts,ends}with vs slices
Handle negative indices similar to slices.
This commit is contained in:
parent
585775bf11
commit
1f68fc7fa5
4 changed files with 70 additions and 45 deletions
|
@ -497,6 +497,10 @@ u'\u4001abc'
|
||||||
>>>
|
>>>
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
\item The \method{startswith()} and \method{endswith()}
|
||||||
|
string methods now have accept negative numbers for
|
||||||
|
start and end parameters.
|
||||||
|
|
||||||
\item Another new string method is \method{zfill()}, originally a
|
\item Another new string method is \method{zfill()}, originally a
|
||||||
function in the \module{string} module. \method{zfill()} pads a
|
function in the \module{string} module. \method{zfill()} pads a
|
||||||
numeric string with zeros on the left until it's the specified width.
|
numeric string with zeros on the left until it's the specified width.
|
||||||
|
|
|
@ -223,6 +223,18 @@ def run_method_tests(test):
|
||||||
test('startswith', 'helloworld', 1, 'lowo', 3, 7)
|
test('startswith', 'helloworld', 1, 'lowo', 3, 7)
|
||||||
test('startswith', 'helloworld', 0, 'lowo', 3, 6)
|
test('startswith', 'helloworld', 0, 'lowo', 3, 6)
|
||||||
|
|
||||||
|
# test negative indices in startswith
|
||||||
|
test('startswith', 'hello', 1, 'he', 0, -1)
|
||||||
|
test('startswith', 'hello', 1, 'he', -53, -1)
|
||||||
|
test('startswith', 'hello', 0, 'hello', 0, -1)
|
||||||
|
test('startswith', 'hello', 0, 'hello world', -1, -10)
|
||||||
|
test('startswith', 'hello', 0, 'ello', -5)
|
||||||
|
test('startswith', 'hello', 1, 'ello', -4)
|
||||||
|
test('startswith', 'hello', 0, 'o', -2)
|
||||||
|
test('startswith', 'hello', 1, 'o', -1)
|
||||||
|
test('startswith', 'hello', 1, '', -3, -3)
|
||||||
|
test('startswith', 'hello', 0, 'lo', -9)
|
||||||
|
|
||||||
test('endswith', 'hello', 1, 'lo')
|
test('endswith', 'hello', 1, 'lo')
|
||||||
test('endswith', 'hello', 0, 'he')
|
test('endswith', 'hello', 0, 'he')
|
||||||
test('endswith', 'hello', 1, '')
|
test('endswith', 'hello', 1, '')
|
||||||
|
@ -238,6 +250,21 @@ def run_method_tests(test):
|
||||||
test('endswith', 'ab', 0, 'ab', 0, 1)
|
test('endswith', 'ab', 0, 'ab', 0, 1)
|
||||||
test('endswith', 'ab', 0, 'ab', 0, 0)
|
test('endswith', 'ab', 0, 'ab', 0, 0)
|
||||||
|
|
||||||
|
# test negative indices in endswith
|
||||||
|
test('endswith', 'hello', 1, 'lo', -2)
|
||||||
|
test('endswith', 'hello', 0, 'he', -2)
|
||||||
|
test('endswith', 'hello', 1, '', -3, -3)
|
||||||
|
test('endswith', 'hello', 0, 'hello world', -10, -2)
|
||||||
|
test('endswith', 'helloworld', 0, 'worl', -6)
|
||||||
|
test('endswith', 'helloworld', 1, 'worl', -5, -1)
|
||||||
|
test('endswith', 'helloworld', 1, 'worl', -5, 9)
|
||||||
|
test('endswith', 'helloworld', 1, 'world', -7, 12)
|
||||||
|
test('endswith', 'helloworld', 1, 'lowo', -99, -3)
|
||||||
|
test('endswith', 'helloworld', 1, 'lowo', -8, -3)
|
||||||
|
test('endswith', 'helloworld', 1, 'lowo', -7, -3)
|
||||||
|
test('endswith', 'helloworld', 0, 'lowo', 3, -4)
|
||||||
|
test('endswith', 'helloworld', 0, 'lowo', -8, -2)
|
||||||
|
|
||||||
test('zfill', '123', '123', 2)
|
test('zfill', '123', '123', 2)
|
||||||
test('zfill', '123', '123', 3)
|
test('zfill', '123', '123', 3)
|
||||||
test('zfill', '123', '0123', 4)
|
test('zfill', '123', '0123', 4)
|
||||||
|
|
|
@ -6,6 +6,9 @@ Type/class unification and new-style classes
|
||||||
|
|
||||||
Core and builtins
|
Core and builtins
|
||||||
|
|
||||||
|
- Fixed string.startswith and string.endswith builtin methods
|
||||||
|
so they accept negative indices. [SF bug 493951]
|
||||||
|
|
||||||
- Fixed a bug with a continue inside a try block and a yield in the
|
- Fixed a bug with a continue inside a try block and a yield in the
|
||||||
finally clause. [SF bug 567538]
|
finally clause. [SF bug 567538]
|
||||||
|
|
||||||
|
|
|
@ -1310,6 +1310,21 @@ _PyString_Join(PyObject *sep, PyObject *x)
|
||||||
return string_join((PyStringObject *)sep, x);
|
return string_join((PyStringObject *)sep, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
string_adjust_indices(int *start, int *end, int len)
|
||||||
|
{
|
||||||
|
if (*end > len)
|
||||||
|
*end = len;
|
||||||
|
else if (*end < 0)
|
||||||
|
*end += len;
|
||||||
|
if (*end < 0)
|
||||||
|
*end = 0;
|
||||||
|
if (*start < 0)
|
||||||
|
*start += len;
|
||||||
|
if (*start < 0)
|
||||||
|
*start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static long
|
static long
|
||||||
string_find_internal(PyStringObject *self, PyObject *args, int dir)
|
string_find_internal(PyStringObject *self, PyObject *args, int dir)
|
||||||
{
|
{
|
||||||
|
@ -1332,16 +1347,7 @@ string_find_internal(PyStringObject *self, PyObject *args, int dir)
|
||||||
else if (PyObject_AsCharBuffer(subobj, &sub, &n))
|
else if (PyObject_AsCharBuffer(subobj, &sub, &n))
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
if (last > len)
|
string_adjust_indices(&i, &last, len);
|
||||||
last = len;
|
|
||||||
if (last < 0)
|
|
||||||
last += len;
|
|
||||||
if (last < 0)
|
|
||||||
last = 0;
|
|
||||||
if (i < 0)
|
|
||||||
i += len;
|
|
||||||
if (i < 0)
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
if (dir > 0) {
|
if (dir > 0) {
|
||||||
if (n == 0 && i <= last)
|
if (n == 0 && i <= last)
|
||||||
|
@ -1763,16 +1769,8 @@ string_count(PyStringObject *self, PyObject *args)
|
||||||
else if (PyObject_AsCharBuffer(subobj, &sub, &n))
|
else if (PyObject_AsCharBuffer(subobj, &sub, &n))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (last > len)
|
string_adjust_indices(&i, &last, len);
|
||||||
last = len;
|
|
||||||
if (last < 0)
|
|
||||||
last += len;
|
|
||||||
if (last < 0)
|
|
||||||
last = 0;
|
|
||||||
if (i < 0)
|
|
||||||
i += len;
|
|
||||||
if (i < 0)
|
|
||||||
i = 0;
|
|
||||||
m = last + 1 - n;
|
m = last + 1 - n;
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
return PyInt_FromLong((long) (m-i));
|
return PyInt_FromLong((long) (m-i));
|
||||||
|
@ -2169,7 +2167,7 @@ string_startswith(PyStringObject *self, PyObject *args)
|
||||||
const char* prefix;
|
const char* prefix;
|
||||||
int plen;
|
int plen;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = -1;
|
int end = INT_MAX;
|
||||||
PyObject *subobj;
|
PyObject *subobj;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
|
if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj,
|
||||||
|
@ -2193,23 +2191,15 @@ string_startswith(PyStringObject *self, PyObject *args)
|
||||||
else if (PyObject_AsCharBuffer(subobj, &prefix, &plen))
|
else if (PyObject_AsCharBuffer(subobj, &prefix, &plen))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* adopt Java semantics for index out of range. it is legal for
|
string_adjust_indices(&start, &end, len);
|
||||||
* offset to be == plen, but this only returns true if prefix is
|
|
||||||
* the empty string.
|
if (start+plen > len)
|
||||||
*/
|
|
||||||
if (start < 0 || start+plen > len)
|
|
||||||
return PyBool_FromLong(0);
|
return PyBool_FromLong(0);
|
||||||
|
|
||||||
if (!memcmp(str+start, prefix, plen)) {
|
if (end-start >= plen)
|
||||||
/* did the match end after the specified end? */
|
return PyBool_FromLong(!memcmp(str+start, prefix, plen));
|
||||||
if (end < 0)
|
|
||||||
return PyBool_FromLong(1);
|
|
||||||
else if (end - start < plen)
|
|
||||||
return PyBool_FromLong(0);
|
|
||||||
else
|
else
|
||||||
return PyBool_FromLong(1);
|
return PyBool_FromLong(0);
|
||||||
}
|
|
||||||
else return PyBool_FromLong(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2228,8 +2218,7 @@ string_endswith(PyStringObject *self, PyObject *args)
|
||||||
const char* suffix;
|
const char* suffix;
|
||||||
int slen;
|
int slen;
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = -1;
|
int end = INT_MAX;
|
||||||
int lower, upper;
|
|
||||||
PyObject *subobj;
|
PyObject *subobj;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
|
if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj,
|
||||||
|
@ -2253,15 +2242,17 @@ string_endswith(PyStringObject *self, PyObject *args)
|
||||||
else if (PyObject_AsCharBuffer(subobj, &suffix, &slen))
|
else if (PyObject_AsCharBuffer(subobj, &suffix, &slen))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (start < 0 || start > len || slen > len)
|
string_adjust_indices(&start, &end, len);
|
||||||
|
|
||||||
|
if (end-start < slen || start > len)
|
||||||
return PyBool_FromLong(0);
|
return PyBool_FromLong(0);
|
||||||
|
|
||||||
upper = (end >= 0 && end <= len) ? end : len;
|
if (end-slen > start)
|
||||||
lower = (upper - slen) > start ? (upper - slen) : start;
|
start = end - slen;
|
||||||
|
if (end-start >= slen)
|
||||||
if (upper-lower >= slen && !memcmp(str+lower, suffix, slen))
|
return PyBool_FromLong(!memcmp(str+start, suffix, slen));
|
||||||
return PyBool_FromLong(1);
|
else
|
||||||
else return PyBool_FromLong(0);
|
return PyBool_FromLong(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue