我正在寻找一种方法,以不区分大小写的方式比较和排序C++中的UTF-8字符串,以便在SQLite中的自定义排序规则函数中使用它.
理想情况下,该方法应与区域设置无关.然而,我不会屏住呼吸,据我所知,校对是非常依赖于语言的,所以任何适用于英语以外语言的东西都可以,即使这意味着切换语言环境.
选项包括使用标准C或C++库或小型(适用于嵌入式系统)和非GPL(适用于专有系统)第三方库.
到目前为止我所拥有的:
strcoll
使用C语言环境和std::collate
/ std::collate_byname
区分大小写.(这些是否有不区分大小写的版本?)
我试图使用POSIX strcasecmp,但它似乎没有为除以外的语言环境定义"POSIX"
在POSIX语言环境中,strcasecmp()和strncasecmp()执行从上到下的转换,然后执行字节比较.结果未在其他语言环境中指定.
事实上,strcasecmp
Linux与GLIBC上的语言环境之间的结果并没有改变.
#include#include #include #include 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,但由于其庞大的尺寸,我们无法在嵌入式平台上使用它.
你真正想要的是逻辑上不可能的.没有与区域设置无关的,不区分大小写的排序字符串的方法.简单的反例是"我"<>"我"?天真的答案是否定的,但在土耳其语中,这些字符串是不相等的."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"吗?它们的长度不一样.然而,对于德国人来说,你必须认为他们是平等的.你能活下去吗?