So überprüfe ich, ob mystring
mit einem String beginnt:
>>> mystring.lower().startswith("he")
True
Das Problem ist, dass mystring
sehr lang ist (Tausende von Zeichen), sodass die lower()
-Operation viel Zeit in Anspruch nimmt.
FRAGE: Gibt es einen effizienteren Weg?
Mein erfolgloser Versuch:
>>> import re;
>>> mystring.startswith("he", re.I)
False
Sie können einen regulären Ausdruck wie folgt verwenden:
In [33]: bool(re.match('he', 'Hello', re.I))
Out[33]: True
In [34]: bool(re.match('el', 'Hello', re.I))
Out[34]: False
Bei einer Zeichenfolge mit 2000 Zeichen ist diese Funktion etwa 20x schneller als 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
Wenn Sie dasselbe Präfix wiederholt abgleichen, kann das Vorkompilieren des regulären Ausdrucks einen großen Unterschied machen:
In [41]: p = re.compile('he', re.I)
In [42]: %timeit p.match(s)
1000000 loops, best of 3: 351 ns per loop
Bei kurzen Präfixen könnte das Herausschneiden des Präfixes aus der Zeichenfolge vor der Umwandlung in Kleinbuchstaben noch schneller sein:
In [43]: %timeit s[:2].lower() == 'he'
1000000 loops, best of 3: 287 ns per loop
Die relativen Zeitpunkte dieser Ansätze hängen natürlich von der Präfixlänge ab. Auf meinem Rechner scheint der Breakeven-Punkt etwa sechs Zeichen zu betragen, was bedeutet, dass der vorkompilierte Regex die schnellste Methode wird.
In meinen Experimenten könnte die Überprüfung jedes Charakters noch schneller sein:
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
Diese Methode funktioniert jedoch nur für Präfixe, die beim Schreiben des Codes bekannt sind, und eignet sich nicht für längere Präfixe.
Wie wäre es damit:
prefix = 'he'
if myVeryLongStr[:len(prefix)].lower() == prefix.lower()
Abhängig von der Leistung von .lower () ist es möglicherweise schneller, die Gleichheit mehrmals zu überprüfen, wenn das Präfix klein genug ist:
s = 'A' * 2000
prefix = 'he'
ch0 = s[0]
ch1 = s[1]
substr = ch0 == 'h' or ch0 == 'H' and ch1 == 'e' or ch1 == 'E'
Timing (mit derselben Zeichenfolge wie 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
Im Vergleich zur bestehenden Methode:
>>> timeit.timeit("s.lower().startswith('he')", "s = 'A' * 2000", number=10000)
0.6162763703208611
= 61.63 us per loop
(Das ist natürlich schrecklich, aber wenn der Code extrem leistungskritisch ist, kann es sich lohnen)
Keine der angegebenen Antworten ist tatsächlich korrekt, sobald Sie etwas außerhalb des Bereichs ASCII berücksichtigen.
In einem Fall ohne Berücksichtigung der Groß- und Kleinschreibung sollte ß
als SS
gleichgesetzt werden, wenn Sie den Case-Mapping-Regeln von Unicode folgen.
Um korrekte Ergebnisse zu erhalten, ist die einfachste Lösung die Installation des Python-Moduls regex , das dem Standard entspricht:
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
Eine andere einfache Lösung besteht darin, ein Tupel an startswith()
für alle Fälle zu übergeben, die erforderlich sind, um z. .startswith(('case1', 'case2', ..))
.
Zum Beispiel:
>>> 'Hello'.startswith(('He', 'HE'))
True
>>> 'HEllo'.startswith(('He', 'HE'))
True
>>>