7

我正在寻找一种方法来以不区分大小写的方式比较和排序 C++ 中的 UTF-8 字符串,以便在 SQLite 的自定义排序规则函数中使用它。

  1. 理想情况下,该方法应该与语言环境无关。但是我不会屏住呼吸,据我所知,排序规则非常依赖于语言,所以任何适用于英语以外的语言的东西都可以,即使这意味着切换语言环境。
  2. 选项包括使用标准 C 或 C++ 库或小型(适用于嵌入式系统)和非 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') == %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
    

附言

是的,我知道ICU ,但由于其巨大的尺寸,我们不能在嵌入式平台上使用它。

4

6 回答 6

7

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

UTF-8 字符串给问题增加了额外的复杂性。如果您有适当的语言环境,它们是完全有效的多字节 char* 字符串。但是 C 和 C++ 标准都没有定义这样的语言环境。请咨询您的供应商(嵌入式供应商太多,抱歉,这里没有通用答案)。因此,您必须选择多字节编码为 UTF-8 的语言环境,才能使 mbscmp 函数正常工作。这当然会影响排序顺序,这取决于语言环境。而且,如果您没有 const char* 为 UTF-8 的语言环境,则根本无法使用此技巧。(据我了解,微软的 CRT 受此困扰。他们的多字节代码只能处理最多 2 个字节的字符;UTF-8 需要 3 个)

wchar_t 也不是标准解决方案。据说它是如此之宽,以至于您不必处理多字节编码,但您的排序规则仍将取决于语言环境 (LC_COLLATE) 。但是,使用 wchar_t 意味着您现在为 const char* 选择不使用 UTF-8 的语言环境。

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

于 2008-10-10T13:28:08.273 回答
0

我认为没有可以使用的标准 C/C++ 库函数。您必须自己动手或使用第 3 方库。可以在此处找到特定于语言环境的排序规则的完整 Unicode 规范:http://www.unicode.org/reports/tr10/警告:这是一个很长的文件)。

于 2008-10-08T02:15:37.507 回答
0

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

于 2008-10-09T12:02:42.717 回答
0

我相信你需要自己动手或使用第三方库。我推荐第三方库,因为需要遵循很多规则才能获得真正的国际支持——最好让专家来处理它们。

于 2008-10-09T13:00:33.647 回答
0

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

不过,您必须先将这些 UTF-8 字节转换为 wchar_t 字符串。这不是很难,因为 UTF-8 编码标准有很好的文档记录。我知道这一点,因为我已经完成了,但我无法与您分享该代码。

于 2008-10-10T11:50:35.290 回答
0

如果您仅使用它为您的语言环境进行搜索和排序,我建议您的函数调用一个简单的替换函数,该函数使用如下表将两个多字节字符串转换为每个字符一个字节:

A -> a
à -> a
á -> a
ß -> ss
Ç -> c
等等

然后只需调用 strcmp 并返回结果。

于 2009-02-16T09:37:57.407 回答