什么是标准化的UTF-8?

时间:2011-10-28 15:14:21

标签: php c unicode unicode-normalization

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

但是,我正在尝试为应用程序找出what this means。例如,在哪些情况下,我想要“规范等价”而不是“兼容性等价”,反之亦然?

7 个答案:

答案 0 :(得分:169)

您从未想过的关于Unicode规范化的一切

规范标准化

Unicode包括多种编码某些字符的方法,最明显的是重音字符。规范化规范化将代码点更改为规范编码形式。生成的代码点应该与原始代码点相同,除非字体或渲染引擎中存在任何错误。

何时使用

因为结果看起来是相同的,所以在存储或显示结果之前对字符串应用规范标准化总是安全的,只要你能够容忍结果与输入的位不相同。

Canonical normalization有两种形式:NFD和NFC。这两者在某种意义上是等价的,即可以在这两种形式之间进行转换而不会丢失。在NFC下比较两个字符串将始终提供与在NFD下比较它们相同的结果。

NFD

NFD完全扩展了角色。这是要计算的更快的规范化形式,但结果是更多的代码点(即使用更多的空间)。

如果您只想比较两个尚未规范化的字符串,除非您知道需要兼容性规范化,否则这是首选的规范化表单。

NFC

在运行NFD算法后,NFC会尽可能重新组合代码点。这需要更长的时间,但会产生更短的字符串。

兼容性规范化

Unicode还包含许多真正不属于但在旧版字符集中使用的字符。 Unicode添加了这些以允许将这些字符集中的文本作为Unicode处理,然后转换回来而不会丢失。

兼容性规范化将这些转换为相应的“真实”字符序列,并执行规范规范化。兼容性标准化的结果可能看起来与原件不同。

包含格式信息的字符将替换为不包含格式信息的字符。例如,字符会转换为9。其他人不涉及格式差异。例如,罗马数字字符将转换为常规字母IX

显然,一旦执行了这种转换,就不可能无损地转换回原始字符集。

何时使用

Unicode Consortium建议考虑兼容性规范化,如ToUpperCase转换。在某些情况下这可能是有用的,但你不应该只是毫不犹豫地应用它。

一个优秀的用例是搜索引擎,因为您可能希望搜索9以匹配

您可能不应该做的一件事是显示将兼容性规范化应用于用户的结果。

NFKC / NFKD

兼容性规范化形式有两种形式NFKD和NFKC。它们与NFD和C之间具有相同的关系。

NFKC中的任何字符串本身也属于NFC,NFKD和NFD也是如此。因此NFKD(x)=NFD(NFKC(x))NFKC(x)=NFC(NFKD(x))

结论

如果有疑问,请遵循规范规范化。根据适用的空间/速度权衡,或根据您与之互操作的要求,选择NFC或NFD。

答案 1 :(得分:38)

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

对于可由多个基本字符序列和组合标记表示的字符(例如,“s,下方点,上方点”与上方点,然后点位于下方或使用已有其中一个点的基本字符) ,NFD也将挑选其中一种(下面首先,因为它发生)

兼容性分解包括许多“不应该”确实是字符的字符,但是因为它们是在传统编码中使用的。普通规范化不会统一这些(为了保持往返完整性 - 这不是组合形式的问题,因为没有遗留编码[除了少数越南编码]两者都使用),但兼容性规范化将会。想象一下东亚编码(或半宽/全宽片假名和字母表)或MacRoman中的“fi”连字中出现的“kg”千克符号。

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

答案 2 :(得分:13)

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

规范化是试图解决这个问题。规范化确保(或至少尝试)所有字符以相同的方式编码 - 或者在需要时使用单独的组合变音符号,或者尽可能使用单个代码点。从比较的角度来看,你选择的很多东西并不重要 - 几乎任何规范化的字符串都会与另一个规范化的字符串正确比较。

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

如果您正在充分利用ICU,则可能需要使用规范的正常形式。如果您尝试自己编写代码(例如)假设代码点等于某个字符,那么您可能需要兼容性正常形式,以尽可能频繁地使用该代码。

答案 3 :(得分:5)

如果两个unicode字符串在规范上等效,则字符串实际上是相同的,只使用不同的unicode序列。例如,Ä可以使用字符Ä或A和combination的组合来表示。

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

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

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

答案 4 :(得分:4)

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

Figures 1 and 2提供了两种等价的良好例子。在兼容性等效性下,看起来子脚本和超脚本形式中的相同数字会比较相等。但我不确定能解决与草书阿拉伯语或旋转字符相同的问题。

Unicode文本处理的难点在于您必须深入思考应用程序的文本处理要求,然后使用可用的工具解决它们。这并没有直接解决您的问题,但更详细的答案需要语言专家为您希望支持的每种语言。

答案 5 :(得分:4)

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

字符“Ç”可以表示为字节序列0xc387。但它也可以用C(0x43)后跟字节序列0x8ccca7来表示。所以你可以说0xc387和0x438ccca7是相同的字符。有效的原因是0x8ccca7是一个组合标记;也就是说它需要它前面的角色(这里是一个C),并修改它。

现在,就规范等价与兼容等价之间的区别而言,我们需要一般地看一下字符。

有两种类型的字符,即通过传达意义的字符,以及带有另一个字符并改变它的那些字符。所以9是一个有意义的角色。超级脚本⁹采用该含义并通过演示改变它。因此,它们具有不同的含义,但它们仍然代表了基本特征。

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

希望有帮助...

答案 6 :(得分:1)

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

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

要避免这个问题......使用什么标准? “扩展UTF8”或“紧凑型UTF8”?
使用“ç”或“c +◌̧。”?

W3C和其他人(例如file names)建议使用“作为规范组成”(记住“最紧凑”的短字符串C)...所以,

标准是 C !有疑问使用 NFC

对于互操作性,对于"convention over configuration" choices,建议使用 NFC 来“封装”外部字符串。例如,要存储规范XML,请将其存储在“FORM_C”中。 W3C的CSV on the Web Working Group also recomend NFC(第7.2节)。

PS:de“FORM_C”是大多数库中的默认表单。防爆。 in PHP's normalizer.isnormalized()

术语“复合形式”(FORM_C)用于表示“字符串处于C规范形式”(NFC转换的结果)并且说使用了变换算法...参见http://www.macchiato.com/unicode/nfc-faq

  

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

     
      
  1. U + 00C5(Å)带上环的拉丁文大写字母A
  2.   
  3. U + 212B(Å)ANGSTROM SIGN
  4.   
  5. U + 0041(A)拉丁文大写字母A + U + 030A(̊)上面的组合
  6.         

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

注意:要测试小字符串(纯UTF-8或XML实体引用)的规范化,可以使用this test/normalize online converter