以下是我检查mystring
是否以字符串开头的方式:
>>> mystring.lower().startswith("he")
True
问题是mystring
很长(数千个字符),因此lower()
操作需要花费很多时间。
问题:有更有效的方法吗?
我未成功的尝试:
>>> import re;
>>> mystring.startswith("he", re.I)
False
答案 0 :(得分:45)
您可以使用正则表达式,如下所示:
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
但是,此方法仅适用于编写代码时已知的前缀,并且不适用于较长的前缀。
答案 1 :(得分:24)
这个怎么样:
prefix = 'he'
if myVeryLongStr[:len(prefix)].lower() == prefix.lower()
答案 2 :(得分:2)
根据.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
(当然,这很可怕,但如果代码对性能非常重要,那么它可能是值得的)
答案 3 :(得分:1)
只要您考虑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
答案 4 :(得分:0)
另一种简单的解决方案是,针对匹配以下情况的所有情况,将一个元组传递给startswith()
。 .startswith(('case1', 'case2', ..))
。
例如:
>>> 'Hello'.startswith(('He', 'HE'))
True
>>> 'HEllo'.startswith(('He', 'HE'))
True
>>>
答案 5 :(得分:0)
在Python 3.8中,最快的解决方案包括切片和比较前缀,如this answer中所建议:
def startswith(a_source: str, a_prefix: str) -> bool:
source_prefix = a_source[:len(a_prefix)]
return source_prefix.casefold() == a_prefix.casefold()
第二快的解决方案使用ctypes(例如_wcsicmp。)注意:这是Windows示例。
import ctypes.util
libc_name = ctypes.util.find_library('msvcrt')
libc = ctypes.CDLL(libc_name)
libc._wcsicmp.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p)
def startswith(a_source: str, a_prefix: str) -> bool:
source_prefix = a_source[:len(a_prefix)]
return libc._wcsicmp(source_prefix, a_prefix) == 0
compiled re
solution是第三快的解决方案,包括编译成本。如果this answer中建议使用regex
模块提供完全的Unicode支持,则该解决方案甚至会更慢。每次连续匹配的成本大约与每个ctypes调用相同。
lower()
和casefold()
昂贵,因为这些函数通过遍历源字符串中的每个字符(无论大小写)并相应地映射它们来创建新的Unicode字符串。 (请参阅:How is the built-in function str.lower()
implemented?)每个字符在该循环中花费的时间会增加,因此,如果要处理短前缀和长字符串,请仅在前缀上调用这些函数。
答案 6 :(得分:0)
另一个选择:
import re
o = re.search('(?i)^we', 'Wednesday')
print(o != None)