Unicode 字符串的等价性很奇怪 (2016)

Lobsters Hottest 新闻

摘要

Unicode 字符串等价性很复杂,尤其是涉及校对规则时,会导致意外的结果,例如删除控制字符和非确定性分组。作者讨论了在数据库系统中正确实现 Unicode 支持所面临的挑战。

<p><a href="https://lobste.rs/s/awaoc2/equivalence_unicode_strings_is_strange">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/29 09:54

# Unicode 字符串的相等性很古怪 来源: http://databasearchitects.blogspot.com/2016/08/equivalence-of-unicode-strings-is.html 最初,HyPer(http://wwww.hyper-db.com/)对字符串采用了一个非常简单的模型:我们确保所有字符串都是有效的 UTF-8(https://en.wikipedia.org/wiki/UTF-8),但除此之外并不真正关心 Unicode 的细节。实际上,这对大多数应用(http://utf8everywhere.org/)来说是一个相当合理的模型。通常我们并不关心字符串的具体结构,而在少数需要关心的地方(例如 strpos(https://www.postgresql.org/docs/9.4/static/functions-string.html)和 substr(https://www.postgresql.org/docs/9.4/static/functions-string.html)),我们会添加一些额外的逻辑来正确处理 UTF-8。 这一切都很好,直到用户开始抱怨排序顺序。当对 UTF-8 进行简单排序时,我们按码点值排序,这通常没问题,但并不总是用户想要的。于是我们增加了对排序规则(collations)的支持:(https://www.postgresql.org/docs/9.1/static/collation.html) ``` select * from foo order by x collate "de_DE"; ``` 然后事情就开始变得有趣了。排序规则(collate)语句可以在查询中的任何位置显式给出,如上所示,也可以在 *create table* 语句中添加,为列提供默认排序规则。因此,基本上在 SQL 语句中每个被排序或比较的值都可以关联一个排序规则。 最初我天真地以为这只会影响比较操作,比如用 strcoll(http://en.cppreference.com/w/cpp/string/byte/strcoll)代替 strcmp(http://en.cppreference.com/w/cpp/string/byte/strcmp),但遗憾的是实际情况要复杂得多。首先,Unicode 排序算法(http://www.unicode.org/reports/tr10/)相当复杂,它会在比较之前将输入序列转换为 3(或 4)级权重的序列;其次,一些疯狂的数据库系统默认使用不区分大小写的排序规则(https://msdn.microsoft.com/en-us/library/ms143726.aspx),而一些用户确实想要这种表现。 为什么说大小写不敏感是疯狂的呢?因为它导致了奇怪的语义。首先,整个 Unicode 权重机制就很奇怪。其中一些古怪之处可以通过使用 ICU(http://site.icu-project.org/)来隐藏,但你仍然会得到奇怪的结果。例如,考虑以下两个字符串(\u 是 Unicode 转义): abc abc\u007F 显然它们是不同的,对吧?好吧,如果你去问 ICU 排序演示(http://demo.icu-project.org/icu-bin/collation.html),它们并不不同,被认为是相等的。原因是 DUCET 表(http://www.unicode.org/Public/UCA/latest/allkeys.txt)中对于 007F 的条目是: ``` 007F ; [.0000.0000.0000] # DELETE (in ISO 6429) ``` 所以这个字符在比较时必须被忽略。还有其他一些码点,它们与普通比较相关,但与不区分重音的排序规则无关,等等。这带来了很多乐趣,特别是当你想实现基于哈希的算法时。 但抛开技术问题不谈,这真是用户想要的吗?用户真的期望这两个字符串相等吗?就像他们显然期望 'abc' 和 'äbc' 在不区分重音的排序规则下比较相等一样?那查询呢?例如: ``` select x,count(*) from (values('abc'),('ABC'),('äbc')) s(x) group by x collate "de_DE_ci_ai"; ``` 这个查询以不区分大小写、不区分重音的方式进行 group by,这意味着三个字符串比较结果相等。那么结果是什么?abc 3?ABC 3?äbc 3?三个都是有效的答案,因为根据所选排序规则,它们是“相等的”。如果查询被并行化到多个线程并且第一个值胜出,结果甚至可能是不确定的。 用户真的想要这样吗?嗯,显然他们想要,至少我被告知如此,而且有些系统甚至默认采用不区分大小写的行为。但我认为这很奇怪,这些查询的语义可能相当令人困惑。

相似文章

文件名的Unicode组合

Lobsters Hottest

本文讨论了在Subversion版本控制系统中,不同操作系统之间Unicode文件名组合(NFC与NFD)面临的挑战,并提出了处理这些差异的解决方案。

我最喜欢的Bug:无效的代理对

Hacker News Top

一篇博客文章,回顾了一个Bug:在CRDT库中,插入相邻的多字节表情符号导致了一个拼接操作,分割了代理对,并静默地破坏了协作编辑器的同步。

超越困惑度:面向字节感知语言模型中的UTF-8有效性

arXiv cs.CL

本文研究了字节级语言模型中训练规模与UTF-8生成可靠性之间的关系,发现UTF-8有效性收敛的速度比困惑度大约慢一倍。作者引入了用于隔离结构有效性的评估协议,并表明可靠的UTF-8生成是一种需要单独评估的独特能力。