2008-10-08 69 views
7

我正在寻找一种方法来以大小写不敏感的方式比较和排列C++中的UTF-8字符串以在custom collation function in SQLite中使用它。用于SQLite的不区分大小写的UTF-8字符串整理(C/C++)

  1. 该方法应该理想情况下是区域独立的。但是,就我所知,我不会屏住呼吸,因为排序规则是非常依赖语言的,所以任何对英语以外的语言都适用的东西都可以,即使它意味着切换语言环境。
  2. 选项包括使用标准C或C++库或小型(适用于嵌入式系统)和非GPL(适用于专有系统)的第三方库。

我有什么至今:

  1. strcoll使用C语言环境和std::collate/std::collate_byname是区分大小写的。 (是否有这些不区分大小写的版本?)
  2. 我试图使用POSIX strcasecmp,但它似乎是not defined"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') == %d\n", s1, s2, strcasecmp(s1, s2)); 
        printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
        assert(setlocale(LC_ALL, "en_AU.UTF-8")); 
        printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
        printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
        assert(setlocale(LC_ALL, "fi_FI.UTF-8")); 
        printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
        printf("strcoll('%s', '%s') == %d\n", 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 
    

PS

是的,我知道关于ICU,但我们不能用它在嵌入式平台上,由于其enormous size

回答

7

你真正想要的是逻辑上不可能的。没有语言环境独立,不区分大小写的字符串排序方式。简单的反例是“我”<>“我”?天真的答案是否定的,但在土耳其这些字符串是不平等的。 “我”被大写为“İ”(U + 130 Latin Capital I,带点在上面)

UTF-8字符串给问题增加了额外的复杂性。如果您有适当的语言环境,则它们是完全有效的多字节字符*字符串。但是C和C++标准都没有定义这样的语言环境;检查你的供应商(太多嵌入式供应商,对不起,这里没有专门的答案)。所以你必须选择一个多字节编码为UTF-8的语言环境,以使mbscmp函数起作用。这当然会影响排序顺序,这与语言环境有关。如果你没有const char *为UTF-8的locale,你根本不能使用这个技巧。 (据我了解,微软的CRT受此影响,他们的多字节代码只能处理高达2个字节的字符; UTF-8需要3个)

wchar_t也不是标准解决方案。它应该是如此之广,以至于您不必处理多字节编码,但您的整理仍将取决于区域设置(LC_COLLATE)。但是,使用wchar_t意味着您现在选择不使用UTF-8作为const char *的语言环境。

完成此操作后,您可以基本上通过将字符串转换为小写字母并将它们进行比较来编写自己的排序。这并不完美。你期望L“ß”== L“ss”吗?它们的长度不一样。然而,对于德国人来说,你必须认为他们是平等的。你能忍受吗?

+2

关于德国“ß”字符(以及所有如此丰富的案例)的例子:这些字符必须已经被“解决”或以其他方式处理过数千次,UTF-8或否。 MS Word一直有一个“切换大小写”功能 - 它在Unicode之前的版本中是如何工作的? WordPerfect如何? 我有和OP一样的问题,除了我在Delphi工作。我见过很多基于Windows sqlite的应用程序,它们执行不区分大小写的SELECT(我猜ORDER BY),无论它们是以英语,德语还是(在我的情况下)波兰语区域安装。试试Firefox :)他们如何做到这一点? – 2009-10-17 23:19:23

0

我不认为有一个标准的C/C++库函数可以使用。您必须自行推出或使用第三方库。可以在这里找到用于区域特定归类的完整Unicode规范:http://www.unicode.org/reports/tr10/警告:这是一个文档)。

0

在Windows上,您可以调用OS函数CompareStringW并使用NORM_IGNORECASE标志。您必须先将UTF-8字符串转换为UTF-16。否则,请看IBM的International Components for Unicode

0

我相信你会需要推出自己的或使用第三方库。我建议第三方图书馆,因为有很多规则需要遵循才能获得真正的国际支持 - 最好是让专家处理他们。

0

我没有以示例代码的形式给出明确的答案,但我应该指出,UTF-8字节流实际上包含Unicode字符,并且您必须使用C/C++运行时库的wchar_t版本。

但是,您必须首先将这些UTF-8字节转换为wchar_t字符串。这并不难,因为UTF-8编码标准是very well documented。我知道这一点,因为我已经做到了,但我无法与您分享这些代码。

0

如果你使用它做搜索和排序的只有你的语言环境,我建议你的函数调用一个简单的替换使用表既像多字节字符串转换成每个字符的人一个字节的功能:

A - >一个
A - >一个
A - >一个
SS - > SS
ç - >ç

然后简单地调用的strcmp并返回结果。

相关问题