У меня есть база 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 |
+---+
Если мы посмотрим на таблицы сравнения из 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, суть та же самая.
if "ë" in string:
string.replace('ë','e')
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Какой код для лазаруса нужно написать , чтобы потом при запросе символы поменялись на UTF-8?
Мне надо вывести все столбцы из бд, но если столбец повторяется то его не выводитьНапример:
Есть массив с моделями телефонов, вывожу все марки в таблицу через foreachПытаюсь разделить модели по производителю через отдельный TR