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 Variation Selector-15 和我的一些眼泪

Lobsters Hottest

一位开发者调查了为什么脚注回链字形在RSS阅读器中渲染为表情符号,探索了用于控制文本与表情符号呈现的Unicode变体选择器(U+FE0E和U+FE0F),并讨论了变体选择器在网页内容中面临的更广泛挑战。

Unicode 的转写规则是图灵完备的

Hacker News Top

Unicode 的转写规则(UTS #35)通过编译2-标签系统被证明是图灵完备的,显示终止问题不可判定。这一结果影响了许多系统中使用的 ICU 库。

当编译器对 UTF-8 意见不一致时

Hacker News Top

深入探讨 utfcpp 库中 UTF-8 解码的优化,揭示 Clang 和 GCC 为 ASCII 快速路径生成不同的汇编代码,从而导致显著的性能差异。

文件名的Unicode组合

Lobsters Hottest

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