web-dev-qa-db-de.com

String-Startzeichen mit Groß- und Kleinschreibung in Python

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
40
Nicolas Raoul

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. 

43
NPE

Wie wäre es damit:

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

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)

2
Alex L

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
1
Voo

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
>>>
0
Aziz Alto