136

ICU 项目(现在也有一个PHP)包含帮助规范化 UTF-8 字符串所需的类,以便在搜索时更容易比较值。

但是,我试图弄清楚对应用程序意味着什么。例如,在哪些情况下我想要“规范等价”而不是“兼容性等价”,或者反之亦然?

4

7 回答 7

192
于 2011-10-28T20:13:31.347 回答
44

某些字符,例如带有重音符号的字母(例如é)可以用两种方式表示 - 单个代码点U+00E9或普通字母后跟一个组合重音符号U+0065 U+0301。普通规范化将选择其中之一来始终表示它(NFC 的单个代码点,NFD 的组合形式)。

对于可以由多个基本字符序列和组合标记表示的字符(例如,“s, dot below, dot above”vs put dot above then dot below or using a base character that has a dots),NFD将也选择其中一个(下面先,碰巧)

兼容性分解包括许多“不应该真的”是字符但因为它们用于旧编码的字符。普通规范化不会统一这些(为了保持往返完整性——这对于组合形式来说不是问题,因为没有遗留编码[除了少数越南编码]同时使用两者),但兼容性规范化会。想像出现在某些东亚编码(或半角/全角片假名和字母表)中的“kg”公斤符号,或 MacRoman 中的“fi”连字。

有关详细信息,请参阅http://unicode.org/reports/tr15/ 。

于 2011-10-28T15:39:19.207 回答
13

正常形式(Unicode,而不是数据库)主要(仅?)处理具有变音符号的字符。Unicode 为某些字符提供了“内置”变音符号,例如 U+00C0、“带有 Grave 的拉丁大写字母 A”。相同的字符可以从“Latin Capital A”(U+0041)和“Combining Grave Accent”(U+0300)创建。这意味着即使两个序列产生相同的结果字符,一个字节一个字节比较将显示它们完全不同。

规范化是处理该问题的一种尝试。规范化确保(或至少尝试)所有字符都以相同的方式编码——要么在需要时全部使用单独的组合变音标记,要么尽可能使用单个代码点。从比较的角度来看,你选择哪个并不重要——几乎任何标准化字符串都会与另一个标准化字符串进行正确比较。

在这种情况下,“兼容性”意味着与假设一个代码点等于一个字符的代码的兼容性。如果你有这样的代码,你可能想要使用兼容性范式。虽然我从未见过它直接说明,但标准形式的名称暗示 Unicode 联盟认为最好使用单独的组合变音符号。这需要更多的智能来计算字符串中的实际字符(以及智能地断开字符串等),但更通用。

如果您正在充分利用 ICU,那么您很可能想要使用规范范式。如果您尝试自己编写代码(例如)假设代码点等于一个字符,那么您可能需要尽可能多地实现这一点的兼容性范式。

于 2011-10-28T15:36:37.360 回答
5

如果两个 unicode 字符串在规范上是等价的,那么这些字符串实际上是相同的,只是使用不同的 unicode 序列。例如 Ä 可以使用字符 Ä 或 A 和 ◌̈ 的组合来表示。

如果字符串只是兼容性等效,则字符串不一定相同,但在某些情况下它们可能相同。例如 ff 可以被认为与 ff 相同。

所以,如果你在比较字符串,你应该使用规范等价,因为兼容性等价不是真正的等价。

但是,如果您想对一组字符串进行排序,则使用兼容性等价可能是有意义的,因为它们几乎相同。

于 2011-10-28T15:38:16.613 回答
5

这实际上相当简单。UTF-8 实际上对同一个“字符”有几种不同的表示形式。(我在引号中使用字符,因为它们在字节方面是不同的,但实际上它们是相同的)。链接文档中给出了一个示例。

字符“Ç”可以表示为字节序列0xc387。但也可以用C(0x43)后跟字节序列0xcca7来表示。所以可以说 0xc387 和 0x43cca7 是同一个字符。起作用的原因是 0xcca7 是一个组合标记;也就是说,它采用前面的字符(C此处为 a),并修改它。

现在,至于规范等价与兼容性等价之间的区别,我们需要看一下一般的字符。

有两种类型的字符,一种是通过value传达含义,另一种是采用另一个字符并对其进行更改。9 是一个有意义的字符。上标 ⁹ 采用该含义并通过演示对其进行更改。所以规范上它们有不同的含义,但它们仍然代表基本字符。

规范等价是字节序列呈现具有相同含义的相同字符的地方。兼容性等效是指字节序列呈现具有相同基本含义的不同字符(即使它可能被更改)。9 和 ⁹ 是兼容性等价的,因为它们都表示“9”,但不是规范等价的,因为它们没有相同的表示。

于 2011-10-28T15:42:44.327 回答
4

规范等价还是兼容性等价与您更相关取决于您的应用程序。考虑字符串比较的 ASCII 方式大致映射到规范等价,但 Unicode 代表了很多语言。我认为假设 Unicode 以一种允许您像对待西欧 ASCII 一样对待它们的方式对所有语言进行编码是不安全的。

图 1 和图 2提供了两种等效类型的良好示例。在兼容性等价下,看起来相同的数字在下标和上标形式中会比较相等。但我不确定是否能解决与草书阿拉伯形式或旋转字符相同的问题。

Unicode 文本处理的硬道理是,您必须深入考虑应用程序的文本处理需求,然后使用可用的工具尽可能地解决它们。这并不能直接解决您的问题,但更详细的答案需要您希望支持的每种语言的语言专家。

于 2011-10-28T15:38:53.583 回答
2

比较字符串的问题:对于大多数应用程序而言,内容相同的两个字符串可能包含不同的字符序列。

请参阅Unicode 的规范等价:如果比较算法很简单(或必须很快),则不执行Unicode 等价。例如,在 XML 规范比较中会出现此问题,请参阅 http://www.w3.org/TR/xml-c14n

为了避免这个问题...使用什么标准?“扩展的 UTF8”还是“紧凑的 UTF8”?
使用“ç”还是“c+◌̧.”?

W3C 和其他人(例如文件名)建议使用“组合为规范”(记住“最紧凑”较短字符串的 C)......所以,

标准是C!有疑问使用NFC

对于互操作性和“约定优于配置”的选择,建议使用NFC来“规范化”外部字符串。例如,要存储规范的 XML,请将其存储在“FORM_C”中。W3C在 Web 工作组上的 CSV也推荐 NFC(第 7.2 节)。

PS: de "FORM_C" 是大多数库中的默认形式。前任。在 PHP 的 normalizer.isnormalized()中。


术语“组合形式FORM_C//www.macchiato.com/unicode/nfc-faq

(...) 以下每个序列(前两个是单字符序列)代表相同的字符:

  1. U+00C5 ( Å ) 带环的拉丁文大写字母 A
  2. U+212B ( Å ) 埃符号
  3. U+0041 ( A ) 拉丁文大写字母 A + U+030A ( ̊ ) 上面的组合环

这些序列被称为规范等价。这些形式中的第一种称为 NFC - 用于规范化形式 C,其中 C 代表compostion。(...) 将字符串 S 转换为 NFC 形式的函数可以缩写为toNFC(S),而测试 S 是否在 NFC 中的函数可以缩写为isNFC(S)


注意:要测试小字符串的规范化(纯 UTF-8 或 XML 实体引用),您可以使用此测试/规范化在线转换器

于 2015-08-15T04:51:50.443 回答