Wie führe ich einen Zeichenfolgenvergleich ohne Berücksichtigung der Groß- und Kleinschreibung durch?

573

Wie kann ich in Python einen Vergleich von Zeichenfolgen ohne Berücksichtigung der Groß- und Kleinschreibung durchführen?

Ich möchte den Vergleich einer regulären Zeichenfolge mit einer Repository-Zeichenfolge auf sehr einfache und pythonische Weise zusammenfassen. Ich möchte auch die Möglichkeit haben, Werte in einem Diktat nachzuschlagen, das von Zeichenfolgen mit regulären Python-Zeichenfolgen gehasht wird.

Kozyarchuk
quelle

Antworten:

595

Angenommen, ASCII-Zeichenfolgen:

string1 = 'Hello'
string2 = 'hello'

if string1.lower() == string2.lower():
    print("The strings are the same (case insensitive)")
else:
    print("The strings are NOT the same (case insensitive)")
Harley Holcombe
quelle
71
Das funktioniert nicht immer. Bedenken Sie zum Beispiel, dass es zwei griechische Sigmen gibt, von denen eines erst am Ende verwendet wird. Die Zeichenfolge Σίσυφος („Sísyphos“ oder besser „Síſyphos“) hat alle drei: Großbuchstaben vorne, Kleinbuchstaben am Ende und Kleinbuchstaben am Ende an dritter Stelle. Wenn Ihre beiden Zeichenfolgen Σίσυφοςund sind ΣΊΣΥΦΟΣ, schlägt Ihr Ansatz fehl, da diese unempfindlich der gleiche Fall sein sollen.
Tchrist
52
@ Die letzten beiden Kommentatoren: Ich denke, es ist fair anzunehmen, dass beide Strings ASCII-Strings sind. Wenn Sie nach einer Antwort auf etwas Aufregenderes suchen, bin ich sicher, dass es da draußen ist (oder Sie können es fragen).
Harley Holcombe
16
Problem: 'ß'.lower() == 'SS'.lower()ist falsch.
Kennytm
11
Griechische Buchstaben sind nicht der einzige Sonderfall! Im US-Englisch ist das Zeichen "i" (\ u0069) die Kleinbuchstabenversion des Zeichens "I" (\ u0049). Das türkische Alphabet ("tr-TR") enthält jedoch ein "I mit einem Punkt" -Zeichen "İ" (\ u0130), das die Großbuchstabenversion von "i" und "I" die Captical-Version von "i without" ist ein Punkt "Zeichen" ı "(\ u0131).
Gqqnbig
20
@HarleyHolcombe Wie ist es sicher (oder fair) anzunehmen, dass die Saiten ASCII sind? In der Frage wurde nicht angegeben, und wenn die Zeichenfolgen zu irgendeinem Zeitpunkt von einem Benutzer eingegeben oder angezeigt werden, sollten Sie die Internationalisierung unterstützen. Unabhängig davon werden neue Programmierer dies lesen und wir sollten ihnen die wirklich richtige Antwort geben.
Ethan Reesor
529

Der Vergleich von Zeichenfolgen auf eine Weise, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird, scheint trivial zu sein, ist es aber nicht. Ich werde Python 3 verwenden, da Python 2 hier unterentwickelt ist.

Das erste, was zu beachten ist, ist, dass das Entfernen von Groß- und Kleinschreibung in Unicode nicht trivial ist. Es gibt Text, für den text.lower() != text.upper().lower()wie "ß":

"ß".lower()
#>>> 'ß'

"ß".upper().lower()
#>>> 'ss'

Aber lassen Sie uns sagen , Sie zu caselessly vergleichen wollte "BUSSE"und "Buße". Heck, Sie möchten wahrscheinlich auch vergleichen "BUSSE"und "BUẞE"gleich - das ist die neuere Kapitalform. Der empfohlene Weg ist casefold:

str. casefold ()

Geben Sie eine casefolded Kopie der Zeichenfolge zurück. Casefolded Strings können für das Matching ohne Gehäuse verwendet werden.

Casefolding ähnelt dem Lowercasing, ist jedoch aggressiver, da alle Case-Unterscheidungen in einer Zeichenfolge entfernt werden sollen. [...]

Nicht nur verwenden lower. Wenn casefoldnicht verfügbar, .upper().lower()hilft das Tun (aber nur etwas).

Dann sollten Sie Akzente berücksichtigen. Wenn Ihr Font-Renderer gut ist, denken Sie wahrscheinlich "ê" == "ê"- aber nicht:

"ê" == "ê"
#>>> False

Dies liegt daran, dass der Akzent auf letzterem ein kombinierender Charakter ist.

import unicodedata

[unicodedata.name(char) for char in "ê"]
#>>> ['LATIN SMALL LETTER E WITH CIRCUMFLEX']

[unicodedata.name(char) for char in "ê"]
#>>> ['LATIN SMALL LETTER E', 'COMBINING CIRCUMFLEX ACCENT']

Der einfachste Weg, damit umzugehen, ist unicodedata.normalize. Sie möchten wahrscheinlich die NFKD- Normalisierung verwenden , können jedoch die Dokumentation überprüfen. Dann tut man es

unicodedata.normalize("NFKD", "ê") == unicodedata.normalize("NFKD", "ê")
#>>> True

Zum Schluss wird dies hier in Funktionen ausgedrückt:

import unicodedata

def normalize_caseless(text):
    return unicodedata.normalize("NFKD", text.casefold())

def caseless_equal(left, right):
    return normalize_caseless(left) == normalize_caseless(right)
Veedrac
quelle
8
Eine bessere Lösung besteht darin, alle Zeichenfolgen bei der Aufnahme zu normalisieren. Dann können Sie nur x.casefold() == y.casefold()Vergleiche durchführen, x == ybei denen die Groß- und Kleinschreibung nicht berücksichtigt wird (und vor allem Groß- und Kleinschreibung beachten).
Abarnert
3
@abarnert Abhängig vom Kontext ist es manchmal besser, die Quelle intakt zu lassen, aber eine Normalisierung im Voraus kann auch späteren Code viel einfacher machen.
Veedrac
3
@Veedrac: Du hast recht, es ist nicht immer angemessen; Wenn Sie in der Lage sein müssen, die ursprüngliche Quelle unverändert auszugeben (z. B. weil Sie mit Dateinamen unter Linux arbeiten, bei denen NKFC und NKFD zulässig sind und explizit voneinander abweichen sollen), können Sie sie natürlich nicht bei der Eingabe transformieren.
Abarnert
7
Unicode Standard Abschnitt 3.13 enthält zwei weitere Definitionen für Vergleiche ohne NFD(toCasefold(NFD(str)))Gehäuse : (D146, kanonisch) auf beiden Seiten und (D147, Kompatibilität) NFKD(toCasefold(NFKD(toCasefold(NFD(X)))))auf beiden Seiten. Es heißt, dass das Innere NFDausschließlich mit einem bestimmten griechischen Akzentcharakter zu tun hat. Ich denke, es geht nur um die Randfälle.
2
Und ein bisschen Spaß mit dem Cherokee-Alphabet, bei dem casefold () in Großbuchstaben geschrieben wird: >>> "ᏚᎢᎵᎬᎢᎬᏒ". Upper () 'ᏚᎢᎵᎬᎢᎬᏒ' >>> "ᏚᎢᎵᎬᎢᎬᏒ". Lower () 'ꮪꭲꮅꭼꭲꭼꮢ' >>> "ᏚᎢᎵᎬᎢᎬᏒ" .casefold () 'ᏚᎢᎵᎬᎢᎬᏒ' >>>
bortzmeyer
60

Rufen Sie mit Python 2 .lower()jede Zeichenfolge oder jedes Unicode-Objekt auf ...

string1.lower() == string2.lower()

... wird die meiste Zeit funktionieren, funktioniert aber in den von @tchrist beschriebenen Situationen nicht .

Angenommen, wir haben eine Datei unicode.txtmit den beiden Zeichenfolgen Σίσυφοςund ΣΊΣΥΦΟΣ. Mit Python 2:

>>> utf8_bytes = open("unicode.txt", 'r').read()
>>> print repr(utf8_bytes)
'\xce\xa3\xce\xaf\xcf\x83\xcf\x85\xcf\x86\xce\xbf\xcf\x82\n\xce\xa3\xce\x8a\xce\xa3\xce\xa5\xce\xa6\xce\x9f\xce\xa3\n'
>>> u = utf8_bytes.decode('utf8')
>>> print u
Σίσυφος
ΣΊΣΥΦΟΣ

>>> first, second = u.splitlines()
>>> print first.lower()
σίσυφος
>>> print second.lower()
σίσυφοσ
>>> first.lower() == second.lower()
False
>>> first.upper() == second.upper()
True

Das Σ-Zeichen hat zwei Kleinbuchstaben, ς und σ, und .lower() hilft nicht, sie ohne Berücksichtigung der Groß- und Kleinschreibung zu vergleichen.

Ab Python 3 werden jedoch alle drei Formulare in ς aufgelöst, und das Aufrufen von lower () für beide Zeichenfolgen funktioniert ordnungsgemäß:

>>> s = open('unicode.txt', encoding='utf8').read()
>>> print(s)
Σίσυφος
ΣΊΣΥΦΟΣ

>>> first, second = s.splitlines()
>>> print(first.lower())
σίσυφος
>>> print(second.lower())
σίσυφος
>>> first.lower() == second.lower()
True
>>> first.upper() == second.upper()
True

Wenn Sie sich also für Randfälle wie die drei griechischen Sigmen interessieren, verwenden Sie Python 3.

(Als Referenz werden Python 2.7.3 und Python 3.3.0b1 in den obigen Interpreter-Ausdrucken gezeigt.)

Nathan Craike
quelle
20
Um den Vergleich noch robuster zu gestalten, können Sie ab Python 3.3 casefold verwenden (z. B. first.casefold () == second.casefold ()). Für Python 2 können Sie PyICU verwenden (siehe auch: icu-project.org/apiref/icu4c/… )
kgriffs
42

In Abschnitt 3.13 des Unicode-Standards werden Algorithmen für das Caseless Matching definiert.

X.casefold() == Y.casefold() implementiert in Python 3 das "Standard-Caseless-Matching" (D144).

Beim Fallfalten bleibt die Normalisierung von Zeichenfolgen nicht in allen Fällen erhalten, und daher muss die Normalisierung durchgeführt werden ( 'å'vs. 'å'). D145 führt "Canonical Caseless Matching" ein:

import unicodedata

def NFD(text):
    return unicodedata.normalize('NFD', text)

def canonical_caseless(text):
    return NFD(NFD(text).casefold())

NFD() wird zweimal für sehr seltene Randfälle mit U + 0345-Zeichen aufgerufen.

Beispiel:

>>> 'å'.casefold() == 'å'.casefold()
False
>>> canonical_caseless('å') == canonical_caseless('å')
True

Es gibt auch Kompatibilitäts-Caseless-Matching (D146) für Fälle wie '㎒'(U + 3392) und "Identifier Caseless Matching", um das Caseless-Matching von Identifikatoren zu vereinfachen und zu optimieren .

jfs
quelle
3
Dies ist die beste Antwort für Python 3, da Python 3 Unicode-Zeichenfolgen verwendet und die Antwort beschreibt, wie der Unicode-Standard die Übereinstimmung von Zeichenfolgen ohne Gehäuse definiert.
SergiyKolesnikov
Leider, wie von Python 3.6, die casefold()nicht Funktion nicht die spezielle Fall der Behandlung von Groß I implementieren und punktierte Groß I , wie in beschriebenem Fall Folding Eigenschaften . Daher kann der Vergleich für Wörter aus türkischen Sprachen, die diese Buchstaben enthalten, fehlschlagen. Zum Beispiel canonical_caseless('LİMANI') == canonical_caseless('limanı')muss zurückkehren True, aber es gibt zurück False. Derzeit besteht die einzige Möglichkeit, in Python damit umzugehen, darin, einen Casefold-Wrapper zu schreiben oder eine externe Unicode-Bibliothek wie PyICU zu verwenden.
SergiyKolesnikov
@SergiyKolesnikov .casefold () verhält sich soweit ich das beurteilen kann so wie es sollte. Aus dem Standard: „Das Gehäuse Operationen Standard ist für den Einsatz in der vorgesehene Abwesenheit von für bestimmte Sprachen und Umgebungen zugeschnitten“ . Die Gehäuseregeln für das türkisch gepunktete Kapital I und das dotless small i finden Sie in SpecialCasing.txt. "Für nicht-türkische Sprachen wird diese Zuordnung normalerweise nicht verwendet." Aus den Unicode-FAQ: F: Warum sind keine zusätzlichen Zeichen codiert, um das Gebietsschema-unabhängige Gehäuse für Türkisch zu unterstützen?
JFS
1
@ jf-sebastian Ich habe nicht gesagt, dass casefold () sich schlecht benimmt. Es wäre nur praktisch, wenn ein optionaler Parameter implementiert würde, der die spezielle Behandlung von Großbuchstaben und gepunkteten Großbuchstaben I ermöglicht. So funktioniert beispielsweise die Funktion foldCase () in der ICU-Bibliothek : "Das Falzen von Groß- und Kleinschreibung ist vom Gebietsschema unabhängig und nicht kontextabhängig -empfindlich, aber es gibt eine Option, ob Zuordnungen für gepunktetes I und punktloses i, die in CaseFolding.txt mit 'T' gekennzeichnet sind, eingeschlossen oder ausgeschlossen werden sollen. "
SergiyKolesnikov
6

Ich habe diese Lösung hier mit Regex gesehen .

import re
if re.search('mandy', 'Mandy Pande', re.IGNORECASE):
# is True

Es funktioniert gut mit Akzenten

In [42]: if re.search("ê","ê", re.IGNORECASE):
....:        print(1)
....:
1

Bei Unicode-Zeichen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird, funktioniert dies jedoch nicht. Vielen Dank an @Rhymoid, dass Sie darauf hingewiesen haben, dass nach meinem Verständnis das genaue Symbol erforderlich ist, damit der Fall wahr ist. Die Ausgabe ist wie folgt:

In [36]: "ß".lower()
Out[36]: 'ß'
In [37]: "ß".upper()
Out[37]: 'SS'
In [38]: "ß".upper().lower()
Out[38]: 'ss'
In [39]: if re.search("ß","ßß", re.IGNORECASE):
....:        print(1)
....:
1
In [40]: if re.search("SS","ßß", re.IGNORECASE):
....:        print(1)
....:
In [41]: if re.search("ß","SS", re.IGNORECASE):
....:        print(1)
....:
Shiwangi
quelle
4
Die Tatsache , dass ßnicht innerhalb gefunden wird SSmit Groß- und Kleinschreibung Suche gibt Hinweise darauf , dass es nicht funktioniert die Arbeit mit Unicode - Zeichen überhaupt .
3

Der übliche Ansatz besteht darin, die Zeichenfolgen für die Suche und Vergleiche in Groß- oder Kleinbuchstaben zu schreiben. Zum Beispiel:

>>> "hello".upper() == "HELLO".upper()
True
>>> 
Andru Luvisi
quelle
2

Wie wäre es zuerst mit der Konvertierung in Kleinbuchstaben? Sie können verwenden string.lower().

Camilo Díaz Repka
quelle
4
Sie können ihre Karten in Kleinbuchstaben nicht vergleichen: Σίσυφοςund ΣΊΣΥΦΟΣwürden kein Äquivalent testen, sollten es aber.
Tchrist
-2
def insenStringCompare(s1, s2):
    """ Method that takes two strings and returns True or False, based
        on if they are equal, regardless of case."""
    try:
        return s1.lower() == s2.lower()
    except AttributeError:
        print "Please only pass strings into this method."
        print "You passed a %s and %s" % (s1.__class__, s2.__class__)
Patrick Harrington
quelle
3
Sie ersetzen eine Ausnahme durch eine auf stdout gedruckte Nachricht und geben dann None zurück, was False ist. Das ist in der Praxis sehr wenig hilfreich.
Gerrit
-2

Alles, was Sie tun müssen, ist, die beiden Zeichenfolgen in Kleinbuchstaben umzuwandeln (alle Buchstaben werden zu Kleinbuchstaben) und sie dann zu vergleichen (vorausgesetzt, die Zeichenfolgen sind ASCII-Zeichenfolgen).

Zum Beispiel:

string1 = "Hello World"
string2 = "hello WorlD"

if string1.lower() == string2.lower():
    print("The two strings are the same.")
else:
    print("The two strings are not the same.")
Rohit Karthik
quelle
Diese Antwort fügt keine neuen Informationen hinzu. Darüber hinaus ist es fast das gleiche wie die akzeptierte Antwort .
Georgy
-3

Dies ist eine weitere Regex, die ich in der letzten Woche lieben / hassen gelernt habe, also normalerweise als (in diesem Fall ja) etwas importieren, das widerspiegelt, wie ich mich fühle! mache eine normale Funktion .... frage nach Eingabe, dann benutze .... etwas = re.compile (r'foo * | spam * ', yes.I) ...... re.I (yes.I unten) ist dasselbe wie IGNORECASE, aber Sie können nicht so viele Fehler machen, wenn Sie es schreiben!

Sie durchsuchen Ihre Nachricht dann mit regulären Ausdrücken, aber ehrlich gesagt sollten dies einige Seiten sein, aber der Punkt ist, dass Foo oder Spam zusammengeleitet werden und Groß- und Kleinschreibung ignoriert wird. Wenn dann einer von beiden gefunden wird, zeigt lost_n_found einen von ihnen an. Wenn beides nicht der Fall ist, ist lost_n_found gleich None. Wenn es nicht gleich none ist, geben Sie den user_input in Kleinbuchstaben mit "return lost_n_found.lower ()" zurück.

Auf diese Weise können Sie alles, was zwischen Groß- und Kleinschreibung unterscheidet, viel einfacher abgleichen. Schließlich steht (NCS) für "niemand kümmert sich ernsthaft ...!" oder nicht zwischen Groß- und Kleinschreibung unterscheiden .... je nachdem

Wenn jemand irgendwelche Fragen hat, melde dich bei mir.

    import re as yes

    def bar_or_spam():

        message = raw_input("\nEnter FoO for BaR or SpaM for EgGs (NCS): ") 

        message_in_coconut = yes.compile(r'foo*|spam*',  yes.I)

        lost_n_found = message_in_coconut.search(message).group()

        if lost_n_found != None:
            return lost_n_found.lower()
        else:
            print ("Make tea not love")
            return

    whatz_for_breakfast = bar_or_spam()

    if whatz_for_breakfast == foo:
        print ("BaR")

    elif whatz_for_breakfast == spam:
        print ("EgGs")
Ali Paul
quelle