Подтвердить что ты не робот

Строка без учета регистра запуска в Python

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

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

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

ВОПРОС: Есть ли более эффективный способ?

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Сроки (с использованием той же строки, что и 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

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

Ответ 4

Ни один из приведенных ответов не является правильным, как только вы считаете что-либо вне диапазона 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

Ответ 5

Другое простое решение состоит в том, чтобы передать кортеж в startswith() для всех случаев, необходимых для соответствия, например .startswith(('case1', 'case2',..)).

Например:

>>> 'Hello'.startswith(('He', 'HE'))
True
>>> 'HEllo'.startswith(('He', 'HE'))
True
>>>