SQLite的案例不敏感的UTF-8字符串排序规则(C / C ++)

时间:2008-10-08 01:12:53

标签: c++ sqlite unicode utf-8 internationalization

我正在寻找一种方法,以不区分大小写的方式比较和排序C ++中的UTF-8字符串,以便在custom collation function in SQLite中使用它。

  1. 该方法理想情况下与语言环境无关。但是我不会屏住呼吸,据我所知,校对是非常依赖于语言的,所以任何适用于英语以外语言的东西都可以,即使这意味着切换语言环境。
  2. 选项包括使用标准C或C ++库或(适用于嵌入式系统)和非GPL (适用于专有系统)第三方库。
  3. 到目前为止我所拥有的:

      带有C语言环境的{li> strcollstd::collate / std::collate_byname区分大小写。 (这些是否有不区分大小写的版本?)
    1. 我尝试使用POSIX strcasecmp,但对"POSIX"以外的区域设置似乎not defined

        

      在POSIX语言环境中,strcasecmp()和strncasecmp()执行上/下转换,然后执行字节比较。其他区域设置中未指定结果。

      事实上,strcasecmp的结果在使用GLIBC的Linux上的语言环境之间没有变化。

      #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
      
    2. P上。 S上。

      是的,我知道ICU,但由于enormous size我们无法在嵌入式平台上使用它。

6 个答案:

答案 0 :(得分:7)

你真正想要的是逻辑上不可能的。没有与区域设置无关的,不区分大小写的排序字符串的方法。简单的反例是“i”&lt;&gt; “一世” ?天真的答案是否定的,但在土耳其语中,这些字符串是不平等的。 “i”大写为“İ”(U + 130 Latin Capital I with dot above)

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

wchar_t也不是标准解决方案。它应该是如此宽,你不必处理多字节编码,但你的排序依然依赖于语言环境(LC_COLLATE)。但是,使用wchar_t意味着您现在选择不使用UTF-8作为const char *的语言环境。

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

答案 1 :(得分:0)

我认为您不能使用标准的C / C ++库函数。您必须自己动手或使用第三方库。可以在此处找到特定于区域设置的排序规则的完整Unicode规范: http://www.unicode.org/reports/tr10/警告:这是文档)。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

如果您只是使用它来搜索和排序您的语言环境,我建议您调用一个简单的替换函数,该函数使用如下表格将每个字符串的多字节字符串转换为一个字节:
A - &gt;一个
à - &gt;一个
á - &gt;一个
ß - &gt; SS
Ç - &gt; ç
等等

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