mystring
が何らかの文字列で始まるかどうかを確認する方法は次のとおりです。
_>>> mystring.lower().startswith("he")
True
_
問題は、mystring
が非常に長い(数千文字)ため、lower()
操作に時間がかかることです。
質問:より効率的な方法はありますか?
私の失敗した試み:
_>>> import re;
>>> mystring.startswith("he", re.I)
False
_
次のように正規表現を使用できます。
_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
_
これらのアプローチの相対的なタイミングは、もちろんプレフィックスの長さに依存します。私のマシンでは、損益分岐点は約6文字のようです。これは、プリコンパイルされた正規表現が最速の方法になるときです。
私の実験では、すべての文字を個別にチェックする方が高速です。
_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()のパフォーマンスに応じて、prefixが十分に小さかった場合、同等性を複数回チェックする方が高速になる場合があります。
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
(もちろんこれは恐ろしいことですが、コードのパフォーマンスが非常に重要な場合は価値があります)
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
別の簡単な解決策は、Tupleをstartswith()
に渡すことです。 .startswith(('case1', 'case2', ..))
。
例えば:
>>> 'Hello'.startswith(('He', 'HE'))
True
>>> 'HEllo'.startswith(('He', 'HE'))
True
>>>