web-dev-qa-db-fra.com

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

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

Ils ont la même valeur:

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

Mais ils ont différentes représentations Unicode:

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 Unicode sur leur valeur?

56
Seunghoon Baek

normalisation Unicode vous y conduira:

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

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

Personnage U+F9FB est un caractère "Compatibilité CJK". Ces caractères se décomposent en un ou plusieurs caractères CJK normaux une fois normalisés.

61
Ry-

Personnage U+F9FB () est un idéogramme de compatibilité CJK . Ces caractères sont des points de code distincts des caractères CJK normaux, mais ils se décomposent en un ou plusieurs caractères CJK normaux une fois normalisés.

Unicode dispose d'un algorithme officiel de classement de chaînes appelé UCA conçu à cet effet. Python n’est pas pris en charge par 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 de nombreux autres, mais certainement pas dans tous les cas, choisir le bon normalisation à appliquer aux deux chaînes avant de comparer fonctionnera, ce qui présente l’avantage du support intégré dans stdlib.

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

48
abarnert

J'aurais utilisé PyICU et sa classe Collator. Mais d’abord, vous devriez penser à quel niveau de 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 génère:

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