不区分大小写的字符串从Python开始

这是我检查mystring是否以某些字符串开头的方式:

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

问题是mystring很长(成千上万个字符),因此lower()操作要花费很多时间。

问题:有没有更有效的方法?

我失败的尝试:

>>> import re;
>>> mystring.startswith("he", re.I)
False
5个解决方案
48 votes

您可以使用如下正则表达式:

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个字符的字符串上,这比lower()快20倍:

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

但是,此方法仅适用于在编写代码时已知的前缀,而不能用于更长的前缀。

NPE answered 2020-08-09T21:17:52Z
25 votes

这个怎么样:

prefix = 'he'
if myVeryLongStr[:len(prefix)].lower() == prefix.lower()
inspectorG4dget answered 2020-08-09T21:18:12Z
2 votes

根据.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

(当然,这太可怕了,但是如果代码对性能至关重要,那么就值得了)

Alex L answered 2020-08-09T21:18:45Z
1 votes

一旦您考虑了ASCII范围以外的任何内容,给出的答案实际上都不正确。

例如,在不区分大小写的比较中,如果您遵循Unicode的大小写映射规则,则应将ß视为等于SS

为了获得正确的结果,最简单的解决方案是按照标准安装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
Voo answered 2020-08-09T21:19:14Z
0 votes

另一个简单的解决方案是将所有元组的元组传递给startswith(),以匹配例如 .startswith(('case1', 'case2', ..))

例如:

>>> 'Hello'.startswith(('He', 'HE'))
True
>>> 'HEllo'.startswith(('He', 'HE'))
True
>>>
Aziz Alto answered 2020-08-09T21:19:38Z
translate from https://stackoverflow.com:/questions/13578916/case-insensitive-string-startswith-in-python