如何在 C++ 中实现与 unicode 无关的大小写不敏感比较

How to achieve unicode-agnostic case insensitive comparison in C++

我有一个要求,其中我的 C++ 代码需要进行不区分大小写的比较,而不用担心字符串是否经过编码,或者涉及的编码类型。该字符串可以是 ASCII 或非 ASCII,我只需要按原样存储它并将其与第二个字符串进行比较,而不用担心是否设置了正确的语言环境等等。

用例:假设我的应用程序接收到一个字符串(假设它是一个文件名),最初是 "Zoë Saldaña.txt" 并按原样存储它。随后,它接收到另一个字符串 "zoë saLdañA.txt",通过使用一些 API,该字符串与第一个字符串之间的比较应该会产生匹配结果。与文件名 "abc.txt" 和 "AbC.txt".

相同

我了解了 IBM 的 ICU 以及它如何默认使用 UTF-16 编码。我很想知道:

  1. 如果 ICU 提供了一种方法来解决我的要求,无论其编码类型如何,都可以无缝处理字符串?

  2. 如果 1. 的答案是否定的,那么,使用 ICU 的 API,将所有字符串(ASCII 和非 ASCII)规范化为 UTF-16 是否安全,然后执行案例-不敏感比较等操作?

  3. 是否有替代方案可以促进这一点?

我读了这个post,但它不太符合我的要求。

谢谢!

这个要求是不可能的。计算机不处理字符,而是处理数字。但是 "case insensitive" 比较是对字符起作用的操作。语言环境决定了哪些数字对应哪些字符,因此是必不可少的。

以上不仅适用于所有编程语言,甚至适用于区分大小写的比较。从字符到数字的映射并不总是唯一的。这意味着比较两个数字是行不通的。可能存在字符 42 等同于字符 43 的语言环境。在 Unicode 中,情况更糟。有 个序列 有不同的长度,但仍然是等价的。 (特别是预组合和分解字符)

如果不知道编码,就无法做到这一点。我将举一个使用法语重音字符和 2 种不同编码的示例:cp850 在西欧地区用作 windows 的 OEM 字符,以及众所周知的 iso-8859-1(也称为 latin1,与 win1252 区别不大ansi 字符集 windows)).

  • 在cp850中,0x96是'û',0xca是'╩',0xea是'Û'
  • 在 latin1 中,0x96 是不可打印的 (*),0xca 是 'Ê',0xea 是 'ê'

所以如果字符串是 cp850 编码,0xea 应该与 0x96 相同,0xca 是不同的字符

但如果字符串是 latin1 编码,0xea 应该与 0xca 相同,0x96 是控制字符

您可以通过我只说我知道的语言找到其他 iso-8859-x 编码的类似示例。

cp1252 0x96 中的

(*) 是 '–' unicode 字符 U+2013 与 'ê'

无关

嗯,首先我必须说,任何处理自然语言文本的程序员都有最大的责任去了解和理解 Unicode。其他古老的 20 世纪编码仍然存在,但像 EBCDIC 和 ASCII 之类的东西甚至无法编码简单的英文文本,这些文本可能包含 façade、naïve 或 fiancée 甚至地理标志、数学符号甚至表情符号——从概念上讲,它们类似于表意文字。世界上大多数人不使用拉丁字符来书写文本。 UTF-8 现在是 Internet 上流行的编码,而 UTF-16 被所有现代操作系统内部使用,包括 Windows,不幸的是,它仍然是错误的。 (例如,NTFS 有一个 decade-long 报告的错误,该错误允许一个目录包含 2 个文件,这些文件的名称看起来完全相同但使用不同的正常形式编码——我在通过 [=28= 同步文件时经常遇到这种情况] 在 Windows 和 MacOS 或 Linux 之间;我所有带有重音字符的文件都会被复制,因为与其他系统不同,Windows 使用不同的规范形式并且只在 GUI 级别规范化文件名,不在文件系统级别。我在 2001 年为 Windows 7 报告了这个问题,并且该错误今天在 Windows 10 中仍然存在。)

如果您还不知道什么是范式,请从这里开始:https://en.wikipedia.org/wiki/Unicode_equivalence

Unicode 对大小写转换有严格的规定,必须严格遵守这些规定才能顺利进行。首先,确保两个字符串使用相同的范式(你应该在输入过程中这样做,Unicode 标准有算法)。请不要重新发明轮子,使用 ICU 标准化和比较设施。它们已经过广泛测试并且可以正常工作。使用它们,IBM 免费提供。

注意:如果您打算比较字符串进行排序,请记住排序规则是 locale-dependant,并且受语言和风景的影响很大。例如,在字典中,这些葡萄牙语单词的顺序完全相同:sabia、sabiá、sábia、sábio。相同的排序规则不适用于地址列表,它会使用语音规则来相邻放置像 Peçanha 和 Pessanha 这样的名字。同样的现象也发生在德语的 ß 和 ss 中。是的,自然语言是不符合逻辑的——或者更确切地说,它的规则并不简单。

C'est la vie。こレガ私たちの世界desu。

对于 UTF-8(或其他 Unicode)编码,可以执行“区域中性”不区分大小写的字符串比较。这种类型的比较在多语言环境应用程序中很有用,例如网络协议(如CIFS)、国际数据库数据等

由于 Unicode 元数据可以清楚地识别哪些字符可以“折叠”to/from哪些 upper/lower 大小写字符,因此操作是可能的。

截至 2007 年,当我最后一次查看时,只有不到 2000 upper/lower 个大小写字符对。也可以生成一个完美的哈希函数来将大写字母转换为小写字母(很可能反之亦然,但我没有尝试过)。

当时我用的是Bob Burtle的perfect hash generator。它在我当时正在处理的 CIFS 实现中运行良好。

没有多少小型、固定的数据集可供您使用完美的哈希生成器。但这是其中之一。 :--)

注意:这是语言环境中立的。所以它不会支持德国电话簿之类的应用程序。有很多应用程序您应该 绝对使用 区域设置感知折叠和整理。但实际上有很多地方中性语言环境更可取。尤其是现在,当人们在如此多的时区和文化之间共享数据时。 Unicode 标准很好地定义了一组良好的共享规则。

如果您不使用 Unicode,则假定您有充分的理由。实际上,如果您必须处理其他字符编码,则您有一个高度区域设置感知的应用程序。在这种情况下,OP 的问题不适用。

另请参阅: