Регистр строке startswith в Python

вот как я проверяю, есть ли mystring начинается с некоторой строки:

>>> mystring.lower().startswith("he")
True

проблема в том, что mystring очень длинный (тысячи символов), поэтому lower() операция занимает много времени.

вопрос: есть ли более эффективный способ?

моя неудачная попытка:

>>> import re;
>>> mystring.startswith("he", re.I)
False

4 ответов


вы можете использовать регулярное выражение следующим образом:

In [33]: bool(re.match('he', 'Hello', re.I))
Out[33]: True 

In [34]: bool(re.match('el', 'Hello', re.I))
Out[34]: False 

на 2000 символов это примерно в 20 раз быстрее, чем lower():

In [38]: s = 'A' * 2000

In [39]: %timeit s.lower().startswith('he')
10000 loops, best of 3: 41.3 us per loop

In [40]: %timeit bool(re.match('el', s, re.I))
100000 loops, best of 3: 2.06 us per loop

если вы повторно сопоставляете один и тот же префикс, предварительная компиляция регулярного выражения может иметь большое значение:

In [41]: p = re.compile('he', re.I)

In [42]: %timeit p.match(s)
1000000 loops, best of 3: 351 ns per loop

для коротких префиксов вырезание префикса из строки перед преобразованием его в нижний регистр может быть еще быстрее:

In [43]: %timeit s[:2].lower() == 'he'
1000000 loops, best of 3: 287 ns per loop

относительные тайминги этих подходов будут курс зависит от длины префикса. На моей машине точка безубытка составляет около шести символов, когда предварительно скомпилированное регулярное выражение становится самым быстрым методом.

в моих экспериментах проверка каждого символа отдельно может быть еще быстрее:

In [44]: %timeit (s[0] == 'h' or s[0] == 'H') and (s[1] == 'e' or s[1] == 'E')
1000000 loops, best of 3: 189 ns per loop

однако этот метод работает только для префиксов, которые известны при написании кода, и не поддается более длинным префиксам.


Как насчет этого:

prefix = 'he'
if myVeryLongStr[:len(prefix)].lower() == prefix.lower()

в зависимости от производительности .lower (), если префикс был достаточно мал, было бы быстрее проверить равенство несколько раз:

s =  'A' * 2000
prefix = 'he'
ch0 = s[0] 
ch1 = s[1]
substr = ch0 == 'h' or ch0 == 'H' and ch1 == 'e' or ch1 == 'E'

Timing (используя ту же строку, что и NPE):

>>> timeit.timeit("ch0 = s[0]; ch1 = s[1]; ch0 == 'h' or ch0 == 'H' and ch1 == 'e' or ch1 == 'E'", "s = 'A' * 2000")
0.2509511683747405

= 0.25 us per loop

по сравнению с существующим способом:

>>> timeit.timeit("s.lower().startswith('he')", "s = 'A' * 2000", number=10000)
0.6162763703208611

= 61.63 us per loop

(Это ужасно, конечно, но если код чрезвычайно важен для производительности, то это может стоить того)


ни один из приведенных ответов на самом деле не является правильным, как только вы рассматриваете что-либо за пределами диапазона ASCII.

например, в регистр сравнения ß следует считать равной SS Если вы следуете правилам сопоставления регистров Unicode.

чтобы получить правильные результаты, самое простое решение-установить Python regex модуль, который соответствует стандарту:

import re
import regex
# enable new improved engine instead of backwards compatible v0
regex.DEFAULT_VERSION = regex.VERSION1 

print(re.match('ß', 'SS', re.IGNORECASE)) # none
print(regex.match('ß', 'SS', regex.IGNORECASE)) # matches