web-dev-qa-db-fra.com

Technique de comparaison de chaînes utilisée par Python

Je me demande comment Python compare les chaînes, en particulier comment il détermine le résultat lorsqu'un opérateur inférieur à (<) ou supérieur à (>) est utilisé.

Par exemple, si je mets print('abc' < 'bac'), j'obtiens True. Je comprends qu’il compare les caractères correspondants dans la chaîne, mais on ne voit pas bien pourquoi, faute de meilleur terme, "poids" est attribué au fait que a est inférieur à b (première position) dans la première chaîne plutôt que le fait que a soit inférieur à b dans la deuxième chaîne (deuxième position).

43
davelupt

De la docs :

La comparaison utilise le lexicographique commande: d'abord les deux premiers articles sont comparés, et s'ils diffèrent ceci détermine le résultat de la Comparaison; si elles sont égales, le les deux éléments suivants sont comparés, et ainsi allumé, jusqu'à ce que l'une des séquences soit épuisé.

Également:

Le classement lexicographique des chaînes utilise le numéro de point de code Unicode pour ordonner des caractères individuels.

ou sur Python 2 :

L'ordre lexicographique pour les chaînes utilise l'ordre ASCII pour les caractères individuels.

Par exemple:

>>> 'abc' > 'bac'
False
>>> ord('a'), ord('b')
(97, 98)

Le résultat False est renvoyé dès que a est inférieur à b. Les autres éléments ne sont pas comparés (comme vous pouvez le constater pour le deuxième élément: b> a est True).

Attention aux lettres minuscules et majuscules:

>>> [(x, ord(x)) for x in abc]
[('a', 97), ('b', 98), ('c', 99), ('d', 100), ('e', 101), ('f', 102), ('g', 103), ('h', 104), ('i', 105), ('j', 106), ('k', 107), ('l', 108), ('m', 109), ('n', 110), ('o', 111), ('p', 112), ('q', 113), ('r', 114), ('s', 115), ('t', 116), ('u', 117), ('v', 118), ('w', 119), ('x', 120), ('y', 121), ('z', 122)]
>>> [(x, ord(x)) for x in abc.upper()]
[('A', 65), ('B', 66), ('C', 67), ('D', 68), ('E', 69), ('F', 70), ('G', 71), ('H', 72), ('I', 73), ('J', 74), ('K', 75), ('L', 76), ('M', 77), ('N', 78), ('O', 79), ('P', 80), ('Q', 81), ('R', 82), ('S', 83), ('T', 84), ('U', 85), ('V', 86), ('W', 87), ('X', 88), ('Y', 89), ('Z', 90)]
69
user225312

La comparaison de chaînes Python est lexicographique:

De la documentation Python: http://docs.python.org/reference/expressions.html

Les chaînes sont comparées lexicographiquement en utilisant les équivalents numériques (le résultat de la fonction intégrée ord ()) de leurs caractères. Les chaînes Unicode et 8 bits sont totalement interopérables dans ce comportement.

Ainsi, dans votre exemple, 'abc' < 'bac', 'a' vient avant (inférieur à) 'b' numériquement (dans ASCII et représentations Unicode), la comparaison se termine donc ici.

8
wkl

Python et pratiquement tous les autres langages informatiques utilisent les mêmes principes que (j'espère) que vous utiliseriez pour trouver un mot dans un dictionnaire imprimé:

(1) En fonction du langage humain impliqué, vous avez une notion d'ordre des caractères: 'a' <'b' <'c' etc. 

(2) Le premier caractère a plus de poids que le deuxième: "az" <"za" (que la langue soit écrite de gauche à droite, de droite à gauche ou de boustrophédon n'a aucune pertinence) 

(3) Si vous manquez de caractères à tester, la chaîne la plus courte est inférieure à la chaîne la plus longue: 'foo' <'food'

Typiquement, dans un langage informatique, la "notion de classement des caractères" est plutôt primitive: chaque caractère a un numéro indépendant du langage humain ord(character) et les caractères sont comparés et triés à l'aide de ce nombre. Souvent, cet ordre n'est pas adapté au langage humain de l'utilisateur et vous devez ensuite vous lancer dans la "compilation", un sujet amusant.

7
John Machin

Regardez aussi Comment trier les chaînes Unicode par ordre alphabétique en Python? où la discussion porte sur le tri des règles données par l'algorithme de classement Unicode ( http://www.unicode.org/reports/tr10/ ).

Répondre au commentaire

Quoi? Comment définir autrement une commande autre que de gauche à droite?

par S.Lott, il existe un contre-exemple célèbre lors du tri de la langue française. Cela implique des accents: en effet, on pourrait dire qu'en français, les lettres sont triées de gauche à droite et les accents de droite à gauche. Voici le contre-exemple: Nous avons e <é et o <ô, vous voudriez donc que les mots cote, coté, côte, côté soient triés par côte <coté <côte <côté>. Eh bien, ce n’est pas ce qui se passe, c’est en fait que vous avez: cote <côte <coté <côté, c’est-à-dire que si nous supprimons "c" et "t", nous obtenons oe <ôe <oé <ôé, ce qui est exactement correct -left commande.

Et une dernière remarque: vous ne devriez pas parler de triage de gauche à droite et de droite à gauche mais plutôt de triage en avant et en arrière.

En effet, il existe des langues écrites de droite à gauche et si vous pensez que l'arabe et l'hébreu sont triés de droite à gauche, vous avez peut-être raison d'un point de vue graphique, mais vous vous trompez au niveau logique!

En effet, Unicode considère les chaînes de caractères codées dans ordre logique, et le sens d'écriture est un phénomène se produisant au niveau des glyphes. En d'autres termes, même si dans le mot שלום, la lettre shin apparaît à droite du caractère, logiquement, elle survient avant. Pour trier ce mot, on considérera d’abord le tibia, puis le mousseux, puis le vav, puis le mem, et c’est forward ordering (bien que l’hébreu soit écrit de droite à gauche), tandis que les accents français sont triés en arrière (bien que le français soit écrit de gauche à droite).

3
yannis

Ceci est un ordre lexicographique . Cela met simplement les choses dans l'ordre du dictionnaire. 

3
Michael J. Barber

Un pur équivalent en Python pour les comparaisons de chaînes serait:

def less(string1, string2):
    # Compare character by character
    for idx in range(min(len(string1), len(string2))):
        # Get the "value" of the character
        ordinal1, ordinal2 = ord(string1[idx]), ord(string2[idx])
        # If the "value" is identical check the next characters
        if ordinal1 == ordinal2:
            continue
        # If it's smaller we're finished and can return True
        Elif ordinal1 < ordinal2:
            return True
        # If it's bigger we're finished and return False
        else:
            return False
    # We're out of characters and all were equal, so the result depends on the length
    # of the strings.
    return len(string1) < len(string2)

Cette fonction fait l'équivalent de la méthode réelle ( Python 3.6 et Python 2.7 ) beaucoup plus lentement. Notez également que l'implémentation n'est pas exactement "Pythonic" et ne fonctionne que pour les comparaisons <. C'est juste pour illustrer comment cela fonctionne. Je n'ai pas vérifié si cela fonctionne comme la comparaison Pythons pour caractères Unicode combinés .

Une variante plus générale serait:

from operator import lt, gt

def compare(string1, string2, less=True):
    op = lt if less else gt
    for char1, char2 in Zip(string1, string2):
        ordinal1, ordinal2 = ord(char1), ord(char1)
        if ordinal1 == ordinal2:
            continue
        Elif op(ordinal1, ordinal2):
            return True
        else:
            return False
    return op(len(string1), len(string2))
2
MSeifert

Les chaînes sont comparées lexicographiquement en utilisant les équivalents numériques (résultat de la fonction intégrée ord ()) de leurs caractères. Les chaînes Unicode et 8 bits sont totalement interopérables dans ce comportement.

1
Senthil Kumaran

Voici un exemple de code comparant deux chaînes lexicographiquement.

  a = str(input())
  b = str(input())
  if 1<=len(a)<=100 and 1<=len(b)<=100:
    a = a.lower()
    b = b.lower()
    if a > b:
       print('1')
    Elif a < b:
       print( '-1')
    Elif a == b:
       print('0') 

pour différentes entrées les sorties sont-

1- abcdefg
   abcdeff
   1

2- abc
   Abc
   0

3- abs
   AbZ
  -1
0
Dlucidone