Регистр строке 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
однако этот метод работает только для префиксов, которые известны при написании кода, и не поддается более длинным префиксам.
в зависимости от производительности .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