Параметры сортировки строк UTF-8 без учета регистра для SQLite (C / C++)

Я ищу метод для сравнения и сортировки строк UTF-8 в C++ без учета регистра, чтобы использовать его в пользовательская функция сортировки в SQLite.

  1. метод в идеале быть независимым от локали. Однако я не буду задерживать дыхание, насколько я знаю, сортировка очень зависит от языка, поэтому все, что работает на языках, отличных от английского, будет делать, даже если это означает переключение локалей.
  2. параметры включите использование стандартной библиотеки C или C++ или маленький (соответствующий для врезанной системы) и non-GPL (подходит для проприетарной системы) сторонняя библиотека.

что у меня пока:

  1. strcoll С локалями C и std::collate/std::collate_byname чувствительны к регистру. (Существуют ли нечувствительные к регистру версии?)
  2. Я пытался использовать 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 и верните результаты.