Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Сравнение слов в Python аналогично тому, как это делается в MySQL при использовании utf8_general_ci

#python #mysql


У меня есть база MySQL с сопоставлением (collation) utf8_general_ci (если точнее,
MariaDB и utf8mb4_general_ci, но, думаю, не суть), из-за чего при сравнениях некоторые
неодинаковые строки считаются равными: например, буквы е и ё считаются одинаковыми.
Однако мне необходимо делать одинаковые сравнения строк и в MySQL, и в Python: строки,
считающиеся равными в MySQL, оказываются неравными в Python, из-за чего в моей программе
иногда случаются сбои.

>>> "еж" == "ёж"
False

MariaDB [(none)]> select 'еж' = 'ёж' as c;
+---+
| c |
+---+
| 1 |
+---+


Каким образом я могу сравнивать или преобразовывать строки в Python так, чтобы поведение
было аналогично utf8_general_ci в MySQL? (Сортировка не интересует — только равно/не
равно.)

Иначе говоря, требуется такое поведение:

>>> "еж" == "ёж"
True


Разумеется, меня интересует не только е/ё, но и всё остальное поведение utf8_general_ci:

MariaDB [(none)]> select 'u' = 'ṻ' as c;
+---+
| c |
+---+
| 1 |
+---+

MariaDB [(none)]> select 'O' = 'ø' as c;
+---+
| c |
+---+
| 0 |
+---+

    


Ответы

Ответ 1



Если мы посмотрим на таблицы сравнения из MySQL, то мы увидим что е и ё в них считаются за одну букву. Там же можно найти другие таблицы сравнения, конкретно нас интересуют таблицы сравнения для кириллицы из ICU. Это то, что нужно, потому что, по счастью, есть модуль для связи с API ICU. Модуль требует сами библиотеки ICU, потом проще будет установить всё так: sudo apt install python3-icu Используем: >>> from icu import Collator, Locale >>> col = Collator.createInstance(Locale('ru')) >>> col.equals(u'ё', u'е') False Это не то, что нам нужно. Такая ситуация существует потому что по умолчанию используются более строгие правила сравнения (сила сравнения в терминах ICU). Конкретно нам нужно установить силу сравнения в значение PRIMARY, чтобы при сравнении игнорировались вторичные отличия такие как точки над буквой. Тогда сравнение работает так, как мы хотим: >>> col.setStrength(Collator.PRIMARY) >>> col.equals(u'ё', u'е') True >>> col.equals(u'я', u'е') False При этом сравнения других букв работают как прежде. Конечно, если требуется сравнение букв за пределами русской локали, то нужно выбирать другую локаль. Можно, например, делать сравнение перебором по нескольким локалям (или по всем), и если хотя бы в одной при сравнении будет True, останавливать перебор. Конечно, этот подход не гарантирует полного 100% соответствия utf8_general_ci, лишь только практическое соответствие результатов сравнения для большинства случаев. Если нужно чтобы всё было точно как в utf8_general_ci, то остаётся только искать те таблицы, которые использует это сравнение, специфичное для MySQL, и изобретать что-то своё на их основе, при этом нужно не забыть про подводные камни нормализации. Есть также вариант не делать сравнения оффлайн, а делать их онлайн, подключившись к MySQL и делая запросы по каждому сравнению. Из вашего же примера: select 'O' = 'ø' as c; Никаких действий с таблицами этот запрос не подразумевает, потому выполняться такие запросы будут настолько быстро, насколько возможно. Это будет самый простой и, одновременно, самый точный способ выполнить задачу, если принципиально чтобы была 100% эквивалентность сравнений. Если сервер MySQL, на котором живёт БД, находится физически на другом сервере, то нет никакой проблемы запустить локальную копию сервера MySQL без баз данных и без ничего, и делать сравнения используя эту локальную копию демона MySQL. Или демона MariaDB, суть та же самая.

Ответ 2



if "ë" in string: string.replace('ë','e')

Комментариев нет:

Отправить комментарий