Comment puis-je comparer une chaîne Unicode qui a des octets différents, mais la même valeur?

Je compare des chaînes Unicode entre des objects JSON.

Ils ont la même valeur:

a = '人口じんこうに膾炙かいしゃする' b = '人口じんこうに膾炙かいしゃする' 

Mais ils ont des représentations Unicode différentes:

 Ssortingng a : u'\u4eba\u53e3\u3058\u3093\u3053\u3046\u306b\u81be\u7099\u304b\u3044\u3057\u3083\u3059\u308b' Ssortingng b : u'\u4eba\u53e3\u3058\u3093\u3053\u3046\u306b\u81be\uf9fb\u304b\u3044\u3057\u3083\u3059\u308b' 

Comment puis-je comparer deux chaînes Unicode sur leur valeur?

La normalisation Unicode vous y conduira pour celui-ci:

 >>> import unicodedata >>> unicodedata.normalize("NFC", "\uf9fb") == "\u7099" True 

Utilisez unicodedata.normalize sur vos deux chaînes avant de les comparer avec == pour vérifier l’équivalence canonique Unicode.

Le caractère U+F9FB est un caractère de “compatibilité CJK”. Ces caractères se décomposent en un ou plusieurs caractères CJK normalisés lorsqu’ils sont normalisés.

Le caractère U+F9FB (炙) est un idéogramme de compatibilité CJK . Ces caractères sont des points de code distincts des caractères CJK standard, mais ils se décomposent en un ou plusieurs caractères CJK normalisés lorsqu’ils sont normalisés.

Unicode a un algorithme officiel de classement de chaînes appelé UCA conçu exactement à cette fin. Python n’est pas fourni avec la prise en charge UCA à partir de la version 3.7, mais il existe des bibliothèques tierces telles que pyuca :

 >>> from pyuca import Collator >>> ck = Collator().sort_key >>> ck(a) == ck(b) True 

Dans ce cas – et dans bien d’autres, mais certainement pas tous – le choix de la normalisation appropriée à appliquer aux deux chaînes avant de comparer fonctionnera, avec l’avantage de prendre en charge le stdlib.

* L’idée a été acceptée en principe depuis la version 3.4, mais personne n’a écrit une implémentation – en partie parce que la plupart des développeurs de base qui se soucient utilisent pyuca ou l’une des deux liaisons ICU, qui ont l’avantage de fonctionner dans les versions actuelles et antérieures. de Python.

J’aurais utilisé PyICU et sa classe Collator. Mais d’abord, vous devriez penser à quel niveau d’ algorithme de classement Unicode vous voulez que l’égalité se produise.

 #!/usr/bin/python # -*- coding: utf-8 -*- from icu import Collator coll = Collator.createInstance() coll.setStrength(Collator.IDENTICAL) a = u'人口じんこうに膾炙かいしゃする' b = u'人口じんこうに膾炙かいしゃする' print repr(a) print repr(b) print ('%s == %s : %s' % (a, b, coll.equals(a,b))) a = u'エレベーター' b = u'エレベーター' print ('%s == %s : %s' % (a, b, coll.equals(a,b))) coll.setStrength(Collator.PRIMARY) print ('%s == %s : %s' % (a, b, coll.equals(a,b))) a = u'hello' b = u'HELLO' coll.setStrength(Collator.PRIMARY) print ('%s == %s : %s' % (a, b, coll.equals(a,b))) coll.setStrength(Collator.TERTIARY) print ('%s == %s : %s' % (a, b, coll.equals(a,b))) 

Cela produit:

 u'\u4eba\u53e3\u3058\u3093\u3053\u3046\u306b\u81be\u7099\u304b\u3044\u3057\u3083\u3059\u308b' u'\u4eba\u53e3\u3058\u3093\u3053\u3046\u306b\u81be\uf9fb\u304b\u3044\u3057\u3083\u3059\u308b'人口じんこうに膾炙かいしゃする == 人口じんこうに膾炙かいしゃする : Trueエレベーター == エレベーター : Falseエレベーター == エレベーター : True hello == HELLO : True hello == HELLO : False