Question

    

Cette question a déjà une réponse ici:

         

Quel est le moyen le plus simple de comparer des chaînes en Python, en ignorant la casse?

Bien sûr, on peut faire (str1.lower () < = str2.lower ()), etc., mais cela a créé deux chaînes temporaires supplémentaires (avec les charges évidentes d'allocation / gc).

Je suppose que je recherche un équivalent à la règle stricmp () de C.

[Un peu plus de contexte est demandé, je vais donc démontrer avec un exemple trivial:]

Supposons que vous souhaitiez trier une longue liste de chaînes. Vous faites simplement theList.sort (). C’est une comparaison de chaînes O (n * log (n)) et aucune gestion de la mémoire (car toutes les chaînes et les éléments de la liste sont une sorte de pointeur intelligent). Vous êtes heureux.

Maintenant, vous voulez faire la même chose, mais ignorez le cas (simplifions et disons toutes les chaînes sont en ascii, les problèmes de paramètres régionaux peuvent donc être ignorés). Vous pouvez faire theList.sort (clé = lambda s: s.lower ()), mais alors vous causez deux nouvelles des allocations par comparaison, en plus de charger le ramasse-miettes avec le duplicata (abaissé) des chaînes. Chacun de ces bruits de gestion de la mémoire est un ordre de grandeur plus lent que la simple comparaison de chaînes.

Maintenant, avec une fonction identique à celle de stricmp (), vous remplissez les fonctions suivantes: theList.sort (cmp = strictmp) et il est aussi rapide et convivial que theList.sort (). Vous êtes à nouveau heureux.

Le problème est que toute comparaison insensible à la casse basée sur Python implique une chaîne implicite. les duplications, donc je m'attendais à trouver des comparaisons basées sur C (peut-être dans la chaîne du module).

Impossible de trouver quelque chose comme ça, d'où la question ici. (J'espère que cela clarifie la question).

Était-ce utile?

La solution

En réponse à votre clarification ...

Vous pouvez utiliser les ctypes pour exécuter la fonction c "strcasecmp &"; Ctypes est inclus dans Python 2.5. Il offre la possibilité d’appeler des DLL et des bibliothèques partagées telles que libc. Voici un exemple rapide (Python sur Linux; voir le lien pour l'aide Win32):

from ctypes import *
libc = CDLL("libc.so.6")  // see link above for Win32 help
libc.strcasecmp("THIS", "this") // returns 0
libc.strcasecmp("THIS", "THAT") // returns 8

peut également vouloir faire référence à la documentation de strcasecmp

Pas vraiment sûr que ce soit plus rapide ou plus lent (non testé), mais c’est un moyen d’utiliser une fonction C pour faire des comparaisons de chaînes sans distinction de casse.

~~~~~~~~~~~~~~~

Code ActiveState - Recette 194371: Chaînes insensibles à la casse est une recette pour la création d'une classe de chaînes ne respectant pas la casse. Cela pourrait être un peu trop fatal pour quelque chose de rapide, mais cela pourrait vous fournir un moyen courant de gérer les chaînes ne faisant pas la distinction entre les majuscules et les minuscules si vous envisagez de les utiliser souvent.

Autres conseils

Voici un point de repère montrant que l'utilisation de <= > est plus rapide que la méthode proposée par la réponse acceptée (str.lower):

#!/usr/bin/env python2.7
import random
import timeit

from ctypes import *
libc = CDLL('libc.dylib') # change to 'libc.so.6' on linux

with open('/usr/share/dict/words', 'r') as wordlist:
    words = wordlist.read().splitlines()
random.shuffle(words)
print '%i words in list' % len(words)

setup = 'from __main__ import words, libc; gc.enable()'
stmts = [
    ('simple sort', 'sorted(words)'),
    ('sort with key=str.lower', 'sorted(words, key=str.lower)'),
    ('sort with cmp=libc.strcasecmp', 'sorted(words, cmp=libc.strcasecmp)'),
]

for (comment, stmt) in stmts:
    t = timeit.Timer(stmt=stmt, setup=setup)
    print '%s: %.2f msec/pass' % (comment, (1000*t.timeit(10)/10))

temps typiques sur ma machine:

235886 words in list
simple sort: 483.59 msec/pass
sort with key=str.lower: 1064.70 msec/pass
sort with cmp=libc.strcasecmp: 5487.86 msec/pass

Ainsi, la version avec libc.strcasecmp est non seulement la plus rapide, mais aussi la plus portable et la plus pythonique parmi toutes les solutions proposées ici. Je n'ai pas décrit l'utilisation de la mémoire, mais l'affiche originale n'a toujours pas donné de raison impérieuse de s'en inquiéter. De plus, qui a dit qu’un appel dans le module libc ne dupliquait aucune chaîne?

NB: La méthode lower() string présente également l’avantage de dépendre de la localisation. Quelque chose que vous ne réussirez probablement pas à faire lorsque vous écrivez votre propre & "; Optimisé &"; Solution. Néanmoins, en raison de bogues et de fonctionnalités manquantes dans Python, ce type de comparaison peut vous donner des résultats erronés dans un contexte unicode.

Votre question implique que vous n’avez pas besoin d’Unicode. Essayez l'extrait de code suivant. si cela fonctionne pour vous, vous avez terminé:

Python 2.5.2 (r252:60911, Aug 22 2008, 02:34:17)
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, "en_US")
'en_US'
>>> sorted("ABCabc", key=locale.strxfrm)
['a', 'A', 'b', 'B', 'c', 'C']
>>> sorted("ABCabc", cmp=locale.strcoll)
['a', 'A', 'b', 'B', 'c', 'C']

Clarification: au cas où ce ne serait pas évident à première vue, locale.strcoll semble être la fonction dont vous avez besoin, en évitant str.lower ou locale.strxfrm & "dupliquer &"; chaînes.

Utilisez-vous cette comparaison dans le chemin très fréquemment exécuté d'une application sensible aux performances élevées? Sinon, utilisez-vous ceci sur des chaînes d'une taille en mégaoctets? Sinon, ne vous inquiétez pas pour la performance et utilisez simplement la méthode .lower ().

Le code suivant montre que pour effectuer une comparaison sans distinction de casse en appelant .lower () sur deux chaînes d'une taille de presque un mégaoctet, il faut environ 0,009 seconde sur mon ordinateur de bureau à 1,8 GHz:

from timeit import Timer

s1 = "1234567890" * 100000 + "a"
s2 = "1234567890" * 100000 + "B"

code = "s1.lower() < s2.lower()"
time = Timer(code, "from __main__ import s1, s2").timeit(1000)
print time / 1000   # 0.00920499992371 on my machine

S'il s'agit bien d'une section de code extrêmement importante et critique en termes de performances, je vous recommande d'écrire une fonction en C et de l'appeler à partir de votre code Python, car cela vous permettra de faire une recherche vraiment efficace, insensible à la casse. Vous trouverez des informations détaillées sur l'écriture de modules d'extension C à l'adresse suivante: https://docs.python.org/extending /extending.html

Je ne trouve pas d'autre moyen intégré de faire de la comparaison sans distinction de casse: Le cuisinier python livre-recette utilise lower ().

Cependant, vous devez faire attention en utilisant low pour les comparaisons à cause du problème de Turc I . Malheureusement, le traitement de Python pour l’Irlande n’est pas bon. & # 305; est converti en I, mais I n'est pas converti en & # 305 ;. & # 304; est converti en i, mais i n'est pas converti en & # 304 ;.

Il n'y a pas d'équivalent intégré à cette fonction que vous voulez.

Vous pouvez écrire votre propre fonction qui convertit chaque caractère à la fois en .lower () pour éviter de dupliquer les deux chaînes, mais je suis sûr que cela demandera beaucoup de ressources processeur et sera extrêmement inefficace.

À moins que vous ne travailliez avec des chaînes extrêmement longues (si longues que cela peut causer un problème de mémoire si elles sont dupliquées), je le garderai simplement et utiliser

str1.lower() == str2.lower()

tout ira bien

Cette question pose 2 choses très différentes:

  1. Quel est le moyen le plus simple de comparer des chaînes en Python, en ignorant la casse?
  2. Je suppose que je recherche un équivalent à la règle stricmp () de C.

Puisque # 1 a déjà été très bien répondu (c'est-à-dire: str1.lower () < str2.lower ()), je vais répondre à la question # 2.

def strincmp(str1, str2, numchars=None):
    result = 0
    len1 = len(str1)
    len2 = len(str2)
    if numchars is not None:
        minlen = min(len1,len2,numchars)
    else:
        minlen = min(len1,len2)
    #end if
    orda = ord('a')
    ordz = ord('z')

    i = 0
    while i < minlen and 0 == result:
        ord1 = ord(str1[i])
        ord2 = ord(str2[i])
        if ord1 >= orda and ord1 <= ordz:
            ord1 = ord1-32
        #end if
        if ord2 >= orda and ord2 <= ordz:
            ord2 = ord2-32
        #end if
        result = cmp(ord1, ord2)
        i += 1
    #end while

    if 0 == result and minlen != numchars:
        if len1 < len2:
            result = -1
        elif len2 < len1:
            result = 1
        #end if
    #end if

    return result
#end def

N'utilisez cette fonction que dans les cas où, dans de nombreux cas, la technique des minuscules sera supérieure.

Je ne travaille qu'avec des chaînes ASCII, je ne sais pas comment cela se comportera avec Unicode.

Quand quelque chose n'est pas bien supporté dans la bibliothèque standard, je cherche toujours un paquet PyPI. Avec la virtualisation et l'omniprésence des distributions Linux modernes, je n'évite plus les extensions Python. PyICU semble faire l'affaire: https://stackoverflow.com/a/1098160/3461

Il existe maintenant une option purement python. Il est bien testé: https://github.com/jtauber/pyuca

Ancienne réponse:

J'aime la solution d'expression régulière. Voici une fonction que vous pouvez copier et coller dans n’importe quelle fonction, grâce au support de la structure de bloc de python.

def equals_ignore_case(str1, str2):
    import re
    return re.match(re.escape(str1) + r'\Z', str2, re.I) is not None

Comme j'ai utilisé match au lieu de recherche, je n'ai pas eu besoin d'ajouter un caret (^) à l'expression régulière.

Remarque: Cela ne fait que vérifier l'égalité, ce qui est parfois nécessaire. Je n'irais pas jusqu'à dire que ça me plait.

Voici comment vous feriez avec re:

import re
p = re.compile('^hello$', re.I)
p.match('Hello')
p.match('hello')
p.match('HELLO')

Il est recommandé de trier les listes de valeurs à l’aide de clés coûteuses à calculer en utilisant le motif & "; décoré &"; Cela consiste simplement à construire une liste de nuplets (clé, valeur) à partir de la liste d'origine et à trier cette liste. Il est alors trivial d’éliminer les clés et d’obtenir la liste des valeurs triées:

>>> original_list = ['a', 'b', 'A', 'B']
>>> decorated = [(s.lower(), s) for s in original_list]
>>> decorated.sort()
>>> sorted_list = [s[1] for s in decorated]
>>> sorted_list
['A', 'a', 'B', 'b']

Ou si vous aimez les one-liners:

>>> sorted_list = [s[1] for s in sorted((s.lower(), s) for s in original_list)]
>>> sorted_list
['A', 'a', 'B', 'b']

Si vous vous inquiétez vraiment du coût d’appel de lower (), vous pouvez simplement stocker des n-uplets de (chaîne réduite, chaîne originale) partout. Les tuples sont le type de conteneur le moins cher en Python. Ils sont également faciles à utiliser et peuvent donc être utilisés comme clés de dictionnaire, membres de jeu, etc.

Je suis presque sûr que vous devez utiliser .lower () ou utiliser une expression régulière. Je ne connais pas de fonction de comparaison de chaînes intégrée, insensible à la casse.

Pour les comparaisons occasionnelles, voire répétées, quelques objets chaîne supplémentaires ne devraient pas être importants, à condition que cela ne se produise pas dans la boucle la plus interne de votre code ou que vous ne disposiez pas de suffisamment de données pour constater l'impact sur les performances. Voyez si vous faites: faire des choses dans un & "Stupide &"; manière est beaucoup moins stupide si vous le faites aussi moins.

Si vous voulez sérieusement continuer à comparer beaucoup de texte sans tenir compte de la casse, vous pouvez conserver les versions minuscules des chaînes pour éviter la finalisation et la recréation, ou normaliser l'ensemble de données en minuscule. Cela dépend bien sûr de la taille de l'ensemble de données. S'il y a relativement peu d'aiguilles et une grande pile de foin, le remplacement des aiguilles par des objets regexp compilés est une solution. Si c’est difficile à dire sans voir un exemple concret.

Vous pouvez traduire chaque chaîne en minuscule une fois - paresseusement uniquement lorsque vous en avez besoin, ou en guise de pré-tri pour le tri si vous savez que vous allez trier toute la collection de chaînes. Il existe plusieurs façons d’attacher cette clé de comparaison aux données réelles en cours de tri, mais ces techniques doivent faire l’objet d’un problème distinct.

Notez que cette technique peut être utilisée non seulement pour traiter les problèmes majuscules / minuscules, mais pour d'autres types de tri, tels que le tri spécifique à l'environnement local ou & "Style de bibliothèque &"; le tri des titres qui ignore les articles principaux et normalise les données avant de les trier.

Utilisez simplement la méthode str().lower(), à moins que des performances élevées ne soient importantes - dans ce cas, écrivez cette méthode de tri en tant qu'extension C.

& "Comment écrire une extension Python " ; semble être une intro décente ..

Plus intéressant encore, Ce guide compare l'utilisation de la bibliothèque de ctypes à la rédaction d'un module C externe (le type est nettement plus lent que l’extension C).

import re
if re.match('tEXT', 'text', re.IGNORECASE):
    # is True

Vous pouvez sous-classer str et créer votre propre classe de chaînes ne respectant pas la casse, mais à mon humble avis, ce serait extrêmement imprudent et créerait bien plus de problèmes que cela ne vaut la peine.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top