Параметры сортировки строк UTF-8 без учета регистра для SQLite (C / C++)
Я ищу метод для сравнения и сортировки строк UTF-8 в C++ без учета регистра, чтобы использовать его в пользовательская функция сортировки в SQLite.
- метод в идеале быть независимым от локали. Однако я не буду задерживать дыхание, насколько я знаю, сортировка очень зависит от языка, поэтому все, что работает на языках, отличных от английского, будет делать, даже если это означает переключение локалей.
- параметры включите использование стандартной библиотеки C или C++ или маленький (соответствующий для врезанной системы) и non-GPL (подходит для проприетарной системы) сторонняя библиотека.
что у меня пока:
-
strcoll
С локалями C иstd::collate
/std::collate_byname
чувствительны к регистру. (Существуют ли нечувствительные к регистру версии?) -
Я пытался использовать POSIX strcasecmp, но, похоже, это не определен для другие локалы, кроме
"POSIX"
в локале POSIX strcasecmp() и strncasecmp () выполняют преобразование сверху вниз, а затем сравнение байтов. Результаты не указаны в других местах.
и, действительно, результат
strcasecmp
не изменяется между локалями в Linux с помощью GLIBC.#include <clocale> #include <cstdio> #include <cassert> #include <cstring> const static char *s1 = "Äaa"; const static char *s2 = "äaa"; int main() { printf("strcasecmp('%s', '%s') == %dn", s1, s2, strcasecmp(s1, s2)); printf("strcoll('%s', '%s') == %dn", s1, s2, strcoll(s1, s2)); assert(setlocale(LC_ALL, "en_AU.UTF-8")); printf("strcasecmp('%s', '%s') == %dn", s1, s2, strcasecmp(s1, s2)); printf("strcoll('%s', '%s') == %dn", s1, s2, strcoll(s1, s2)); assert(setlocale(LC_ALL, "fi_FI.UTF-8")); printf("strcasecmp('%s', '%s') == %dn", s1, s2, strcasecmp(s1, s2)); printf("strcoll('%s', '%s') == %dn", s1, s2, strcoll(s1, s2)); }
это напечатано:
strcasecmp('Äaa', 'äaa') == -32 strcoll('Äaa', 'äaa') == -32 strcasecmp('Äaa', 'äaa') == -32 strcoll('Äaa', 'äaa') == 7 strcasecmp('Äaa', 'äaa') == -32 strcoll('Äaa', 'äaa') == 7
П. С.
и да, я знаю о ICU, но мы не можем использовать его на встроенной платформе из-за его огромные размеры.
6 ответов
то, что вы действительно хотите, логически невозможно. Не существует независимого от локали, нечувствительного к регистру способа сортировки строк. Простой встречный пример - " i " "I"? Наивный ответ-нет, но в турецком языке эти строки неравны. "i" в верхнем регистре до " I " (U + 130 Латинская столица I с точкой выше)
строки UTF-8 добавляют дополнительную сложность к вопросу. Они являются вполне допустимыми многобайтовыми строками char*, если у вас есть соответствующая локаль. Но ни c, ни стандарт C++ определяет такую локаль; проверьте у своего поставщика (слишком много встроенных поставщиков, извините, здесь нет ответа genearl). Поэтому вам нужно выбрать локаль, многобайтовая кодировка которой UTF-8, для работы функции mbscmp. Это, конечно, влияет на порядок сортировки, который зависит от локали. И если у вас нет локали, в которой const char* является UTF-8, вы не можете использовать этот трюк вообще. (Насколько я понимаю, CRT Microsoft страдает от этого. Их многобайтовый код обрабатывает только символы до 2 байт; UTF-8 нуждается 3)
wchar_t также не является стандартным решением. Предполагается, что он настолько широк, что вам не нужно иметь дело с многобайтовыми кодировками, но ваши параметры сортировки по-прежнему будут зависеть от locale (LC_COLLATE) . Однако использование wchar_t означает, что теперь вы выбираете локали, которые не используют UTF-8 для const char*.
сделав это, вы можете в основном написать свой собственный заказ, Преобразуя строки в нижний регистр и сравнивая их. Это не идеально. Вы ожидаете L " ß "= = L "ss"? Они даже не такой же длины. Тем не менее, для немца вы должны считать их равными. Ты можешь жить с этим?
Я не думаю, что есть стандартная функция библиотеки C / C++, которую вы можете использовать. Вам придется свернуть свой собственный или использовать стороннюю библиотеку. Полную спецификацию Unicode для локали сортировка может быть найден здесь: http://www.unicode.org/reports/tr10/ (предупреждение: это долго документ).
в Windows вы можете вызвать откат на функцию ОС CompareStringW и использовать флаг NORM_IGNORECASE. Сначала вам нужно преобразовать строки UTF-8 в UTF-16. В противном случае взгляните на IBM международные компоненты для Unicode.
Я считаю, что вам нужно будет свернуть свой собственный или использовать стороннюю библиотеку. Я рекомендую стороннюю библиотеку, потому что есть много правил, которые необходимо соблюдать, чтобы получить настоящую международную поддержку - лучше всего позволить кому-то, кто является экспертом, иметь с ними дело.
У меня нет окончательного ответа в виде примера кода, но я должен указать, что UTF-8 bytestream содержит, по сути, символы Unicode, и вы должны использовать версии wchar_t библиотеки времени выполнения C/C++.
вы должны сначала преобразовать эти байты UTF-8 в строки wchar_t. Это не очень сложно, так как стандарт кодирования UTF-8 является очень хорошо документированы. Я знаю это, потому что я сделал это, но я не могу поделиться кодом с вами.
Если вы используете его для поиска и сортировки только для своей локали, я предлагаю вашей функции вызвать простую функцию замены, которая преобразует обе многобайтовые строки в один байт на char, используя таблицу, такую как:
А - > а
à - > a
á - > a
ß - > ss
Ç - > c
и так далее
затем просто вызовите strcmp и верните результаты.