Как сравнить строку Unicode с разными байтами, но такое же значение?

Я сравниваю строки Unicode между объектами JSON.

Они имеют одинаковое значение:

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

Но у них разные представления 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'

Как я могу сравнить две строки Unicode с их значением?

Ответы

Ответ 1

Нормализация Юникода приведет вас туда для этого:

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

Используйте unicodedata.normalize для обеих строк, прежде чем сравнивать их с == чтобы проверить эквивалентность Unicode для канонических.

Символ U+F9FB является U+F9FB "Совместимость CJK". Эти символы разлагаются на один или несколько регулярных символов CJK при нормализации.

Ответ 2

Символ U+F9FB (炙) - идеограф совместимости CJK. Эти символы являются отличными кодовыми точками от обычных символов CJK, но при нормализации они разлагаются на один или несколько регулярных символов CJK.

Unicode имеет официальный алгоритм сопоставления строк под названием UCA, разработанный именно для этой цели. Python не поддерживает поддержку UCA с 3,7 *, но есть сторонние библиотеки, такие как pyuca:

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

Для этого случая - и многие другие, но определенно не все - выбор соответствующей нормализации для применения к обеим строкам, прежде чем сравнение будет работать, и имеет преимущество поддержки, встроенной в stdlib.

* Идея была принята в принципе с 3,4, но никто не написал реализацию, частично потому, что большинство основных разработчиков, которые заботятся, используют pyuca или одно из двух привязок ICU, которые имеют преимущество в работе в текущей и более старой версиях Python.

Ответ 3

Я бы использовал PyICU и его класс Collator. Но сначала вы должны подумать, на каком уровне алгоритма сортировки Unicode вы хотите, чтобы это произошло.

#!/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)))

Эти результаты:

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