web-dev-qa-db-fra.com

Comment puis-je faire une comparaison de chaîne sans distinction de casse?

Comment puis-je faire une comparaison de chaînes insensible à la casse en Python?

Je voudrais encapsuler la comparaison d'une chaîne normale à une chaîne de référentiel d'une manière très simple et pythonique. Je voudrais aussi avoir la possibilité de rechercher des valeurs dans un dict haché par des chaînes en utilisant des chaînes python normales.

470
Kozyarchuk

En supposant que ASCII chaînes:

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)")
501
Harley Holcombe

Comparer une chaîne sans tenir compte de la casse semble une tâche triviale, mais ce n'est pas le cas. Je vais utiliser Python 3, car Python 2 est sous-développé ici.

La première chose à noter est que les conversions avec suppression de casse en Unicode ne sont pas triviales. Il y a du texte pour lequel text.lower() != text.upper().lower(), tel que "ß":

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

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

Mais supposons que vous vouliez comparer "BUSSE" et "Buße" sans faille. Heck, vous voulez probablement aussi comparer "BUSSE" et "BUẞE" égaux - c'est la nouvelle forme de capital. La méthode recommandée consiste à utiliser casefold:

help(str.casefold)
#>>> Help on method_descriptor:
#>>>
#>>> casefold(...)
#>>>     S.casefold() -> str
#>>>     
#>>>     Return a version of S suitable for caseless comparisons.
#>>>

Ne vous contentez pas d’utiliser lower. Si casefold n'est pas disponible, exécuter .upper().lower() aide (mais seulement un peu).

Ensuite, vous devriez considérer les accents. Si votre rendu de police est bon, vous pensez probablement "ê" == "ê" - mais ce n'est pas le cas:

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

C'est parce qu'ils sont réellement

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']

Le moyen le plus simple de gérer ceci est unicodedata.normalize. Vous voudrez probablement utiliser la normalisation NFKD, mais n'hésitez pas à consulter la documentation. Alors on fait

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

Pour finir, voici ce qui s’exprime en fonctions:

import unicodedata

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

def caseless_equal(left, right):
    return normalize_caseless(left) == normalize_caseless(right)
412
Veedrac

En utilisant Python 2, appelez .lower() sur chaque chaîne ou objet Unicode ...

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

... fonctionnera la plupart du temps, mais ne fonctionne pas dans les situations décrites par @tchrist .

Supposons que nous ayons un fichier appelé unicode.txt contenant les deux chaînes Σίσυφος et ΣΊΣΥΦΟΣ. Avec 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

Le caractère Σ a deux formes minuscules, ς et σ, et .lower() ne vous aidera pas à les comparer sans distinction de casse.

Cependant, à partir de Python 3, les trois formes seront résolues en ς, et l'appel de lower () sur les deux chaînes fonctionnera correctement:

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

Donc, si vous vous souciez des cas Edge comme les trois sigmas en grec, utilisez Python 3.

(Pour référence, Python 2.7.3 et Python 3.3.0b1 sont indiqués dans les impressions d'interprète ci-dessus.)

57
Nathan Craike

Section 3.13 de la norme Unicode définit des algorithmes pour la correspondance sans cas.

X.casefold() == Y.casefold() in Python 3 implémente la "correspondance sans cas par défaut" (D144).

Le casage ne préserve pas la normalisation des chaînes dans toutes les instances et la normalisation doit donc être effectuée ('å' vs. 'å'). Le D145 introduit la "correspondance canonique sans cas":

import unicodedata

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

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

NFD() est appelée deux fois pour les très rares cas Edge impliquant le caractère U + 0345.

Exemple:

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

Il existe également des correspondances de compatibilité sans cas (D146) pour des cas tels que '㎒' (U + 3392) et la "correspondance sans identifiant" pour simplifier et optimiser correspondance des identifiants sans cas .

27
jfs

J'ai vu cette solution ici en utilisant regex .

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

Ça marche bien avec les accents

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

Cependant, cela ne fonctionne pas avec les caractères unicode insensibles à la casse. Merci à @Rhymoid d’avoir signalé que, comme j’ai bien compris, il faut le symbole exact pour que le cas soit vrai. La sortie est la suivante:

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)
....:
7
Shiwangi

Que diriez-vous de convertir en minuscule en premier? vous pouvez utiliser string.lower().

3
Camilo Díaz Repka

L'approche habituelle consiste à mettre les chaînes en majuscules ou en minuscules pour les recherches et les comparaisons. Par exemple:

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

C’est une autre expression rationnelle que j’ai appris à aimer/à haïr au cours de la semaine écoulée et qui importe donc habituellement (dans ce cas oui) quelque chose qui reflète à quel point je me sens! faire une fonction normale .... demander une entrée, puis utiliser .... quelque chose = re.compile (r'foo * | spam * ', yes.I) ...... re.I (yes.I ci-dessous) est identique à IGNORECASE mais vous ne pouvez pas commettre autant d’erreurs en l’écrivant!

Vous recherchez ensuite votre message en utilisant regex, mais honnêtement, il devrait contenir quelques pages, mais le fait est que foo ou spam sont rassemblés et que la casse est ignorée. Ensuite, si l'un ou l'autre est trouvé, lost_n_found en affichera un. si ni alors lost_n_found est égal à Aucun. Si ce n'est pas égal à none, retourne user_input en minuscule avec "return lost_n_found.lower ()"

Cela vous permet de faire correspondre beaucoup plus facilement tout ce qui va être sensible à la casse. Enfin, NCS signifie "personne ne se soucie sérieusement ...!" ou pas sensible à la casse .... selon

si quelqu'un a des questions me faire sur ce ..

    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")
0
Ali Paul