SQLite 的不区分大小写的 UTF-8 字符串排序规则 (C/C++)
Case-insensitive UTF-8 string collation for SQLite (C/C++)
我正在寻找一种方法,以不区分大小写的方式比较和排序C++中的 UTF-8 字符串,以便在 SQLite 中的自定义排序规则函数中使用它。
- 理想情况下,该方法应与区域设置无关。但是,我不会屏住呼吸,据我所知,排序规则非常依赖于语言,因此任何适用于英语以外的语言都可以,即使这意味着切换区域设置。
- 选项包括使用标准的C或C++库或小型(适用于嵌入式系统)和非GPL(适用于专有系统)第三方库。
到目前为止,我拥有的:
- 具有 C 语言环境和
std::collate
/std::collate_byname
的strcoll
区分大小写。(这些版本是否不区分大小写? -
我尝试使用 POSIX strcasecmp,但它似乎没有为
"POSIX"
以外的语言环境定义在 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') == %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,但由于其巨大的规模,我们不能在嵌入式平台上使用它。
你真正想要的在逻辑上是不可能的。没有独立于区域设置、不区分大小写的字符串排序方法。简单的反例是"i"<>"I"?天真的答案是否定的,但在土耳其语中,这些字符串是不相等的。"i"大写为"İ"(U+130 拉丁语大写字母 I,上面有点)
UTF-8 字符串增加了问题的复杂性。如果您有合适的区域设置,它们是完全有效的多字节字符* 字符串。但是 C 和 C++ 标准都没有定义这样的语言环境;请与您的供应商联系(嵌入式供应商太多,抱歉,这里没有通用答案)。因此,您必须选择一个多字节编码为 UTF-8 的区域设置,以便 mbscmp 函数正常工作。这当然会影响排序顺序,排序顺序取决于区域设置。如果您没有 const char* 为 UTF-8 的区域设置,则根本无法使用此技巧。(据我了解,Microsoft的CRT受此影响。他们的多字节代码最多只能处理 2 个字节的字符;UTF-8 需要 3)
wchar_t也不是标准解决方案。据说它是如此之宽,以至于您不必处理多字节编码,但您的排序规则仍然取决于区域设置 (LC_COLLATE)。但是,使用 wchar_t 意味着您现在选择不使用 UTF-8 作为 const char* 的区域设置。
完成此操作后,您基本上可以通过将字符串转换为小写并进行比较来编写自己的排序。这并不完美。你期望 L"ß" == L"ss" 吗?它们甚至不是相同的长度。然而,对于一个德国人来说,你必须认为他们是平等的。你能忍受吗?
我认为没有可以使用标准的C/C++库函数。 您必须自己滚动或使用第 3 方库。 特定于区域设置的排序规则的完整 Unicode 规范可以在这里找到:http://www.unicode.org/reports/tr10/(警告:这是一个很长的文档)。
在Windows上,您可以调用操作系统函数CompareStringW并使用NORM_IGNORECASE标志。您必须先将 UTF-8 字符串转换为 UTF-16。否则,请查看 IBM 的 Unicode 国际组件。
我相信你需要自己动手或使用第三方库。 我推荐第三方图书馆,因为要获得真正的国际支持,需要遵循很多规则 - 最好让专家来处理它们。
我没有示例代码形式的明确答案,但我应该指出,UTF-8 字节流实际上包含 Unicode 字符,您必须使用 C/C++ 运行时库的wchar_t版本。
不过,您必须先将这些 UTF-8 字节转换为wchar_t字符串。这不是很难,因为 UTF-8 编码标准有很好的文档记录。我知道这一点,因为我已经做到了,但我不能与你分享这段代码。
如果您仅使用它来搜索和排序您的语言环境,我建议您的函数调用一个简单的替换函数,该函数使用类似:
A -> a
à -> a
á -> a
ß -> ss
Ç -> c
等等
然后只需调用 strcmp 并返回结果。
- 此代码是否违反一个定义规则
- 生成文件不对文件使用隐式规则
- 变量可能尚未初始化[MIRA 2012规则9.1,强制性]
- 静态结构和一个定义规则
- 尽管遵循了规则,内存泄漏在哪里
- 这是关于成员访问规则的正确摘要吗
- uint_not_usable_without_attribute在业力规则中使用数字生成器时静态断言失败
- 增强精神解析器规则以检测语句中的特殊结尾
- 制作文件:没有规则来制定目标:如何设置正确的规则?
- 为什么此指针值不能转换为整数的规则是什么?
- 传递通用函数,用于梯形规则的数值积分
- C++内存模型中的确切规则阻止在获取操作之前重新排序
- 模板如何影响C++中隐式声明的规则?
- antlr 规则上下文是否可以独立于目标
- Bison/flex 在识别规则后等待输入
- 生成文件中隐式规则中的 -c 标志出错
- 单链接列表实现,规则为 3
- 指针算术规则中的"possibly-hypothetical"是什么意思?
- 假设声明中某些上下文中需要的名称查找规则是什么
- C++ 用于检查容器类中是否存在函数和隐式推导规则的概念