58 votes

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

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

Ils ont la même valeur :

a = ''
b = ''

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

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

Comment puis-je comparer deux chaînes de caractères Unicode sur leur valeur ?

3 votes

Il serait vraiment utile de formater correctement ce document, y compris les guillemets fermants sur vos chaînes de caractères, etc., afin que nous puissions simplement le copier-coller et le tester.

6 votes

Mais je vais supposer que le problème est la normalisation Unicode

2 votes

Pourquoi est-ce que \u7099 la même chose que \uf9fb ?

62voto

minitech Points 87225

Normalisation Unicode vous y amènera 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.

Caractère U+F9FB est un caractère de "compatibilité CJK". Ces caractères se décomposent en un ou plusieurs caractères CJK réguliers lorsqu'ils sont normalisés.

5 votes

Ry, pourquoi as-tu annulé ma révision ? Suggérer aux gens de convertir en NFC et de comparer est "souvent suffisant", mais ce n'est pas vraiment correct. L'UCA existe pour de bonnes raisons, et le fait qu'elle ne soit pas dans la stdlib ne signifie pas qu'il n'y a pas d'implémentations facilement disponibles.

15 votes

@abarnert : Je n'aime pas recommander des bibliothèques dont je n'ai pas examiné les sources, et il n'est pas clair si cela est requis ici ; sentez-vous libre d'écrire votre propre réponse. (De plus, il faut plus d'instructions pour utiliser pyuca de manière efficace) (EDIT : Il s'avère que ce n'est pas le cas et que je pensais à PyICU, que la réponse de wilx couvre maintenant, alors lisez-la).

2 votes

Il faut vraiment insister sur le fait que cela ne résoudra pas toutes les catégories de problèmes de ce genre, et que == est un dur problème auquel vous devez réfléchir. Qu'est-ce que == signifie dans chaque contexte spécifique Il faut y réfléchir. Rendu identique ? Signifie la même chose pour un locuteur d'une langue spécifique ? De toutes les langues ? S'agit-il d'un identifiant ? Voulez-vous ignorer les différences de rendu ? S'agit-il d'une surface d'attaque ?

48voto

abarnert Points 94246

Caractère U+F9FB () est un Idéogramme de compatibilité du CJK . Ces caractères sont des points de code distincts des caractères CJK réguliers, mais ils se décomposent en un ou plusieurs caractères CJK réguliers lorsqu'ils sont normalisés.

Unicode dispose d'un algorithme officiel de collation des chaînes de caractères appelé UCA conçu exactement dans ce but. Python ne supporte pas l'UCA à partir de la version 3.7, * mais il existe des bibliothèques tierces comme pyuca :

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

Dans ce cas - et dans beaucoup d'autres, mais certainement pas tous - choisir l'option appropriée pour l'accès à l'information. normalisation à appliquer aux deux chaînes avant de les comparer fonctionnera, et elle a l'avantage d'un support intégré à la stdlib.

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

0 votes

Merci de me dire quel est le problème de mes tests. J'ai résolu le test de comparaison unicode pour normaliser la chaîne unicode.

3voto

Václav Zeman Points 7911

J'aurais utilisé PyICU et sa classe Collator. Mais tout d'abord, vous devez penser à quel niveau de Algorithme de collationnement d'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)))

Ces sorties :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X