什么是标准化的UTF-8?


129

ICU项目(也现在有一个PHP库)中包含有需要帮助恢复正常UTF-8串类,使搜索时更容易地比较值。

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


230
wOcth͡o̸͢͢͡k̵͟n̴͘ǫw̸̛s̀́͘w͘͢ḩ̵a҉̡͢t ̧̕h́o̵r͏̵rors̶̡͡͠lį̶e̶͟͟͝in͢͏t̕h̷̡͟e͟͟d̛a͜r̕͡k̕͡͡h̴e͏a̷̢̡rt́͏̴̷͠ò̵̶f̸u̧͘ní̛͜c͢͏o̷͏d̸͢eRobot
Obsc

@ObscureRobot我真的很想知道那些额外的符号是否可以具有状态
eonil

1
@Eonil-我不确定unicode上下文中的状态是什么。
ObscureRobot

@ObscureRobot例如,一些代码点是这样的:(begin curved line) (char1) (char2) … (charN) (end curved line)而不是这样的:(curved line marker prefix) (char1) (curved line marker prefix) (char2) (curved line marker prefix) (char2)。换句话说,可以渲染的最小单位是多少?
2013年

2
听起来这本身就是一个好问题。
ObscureRobot

Answers:


181

关于Unicode标准化的所有您想知道的内容

规范化归一化

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

何时使用

因为结果看起来相同,所以在存储或显示字符串之前对字符串进行规范化归类总是安全的,只要您可以容忍结果与输入不完全相同即可。

规范化归纳有两种形式:NFD和NFC。从一个人可以在这两种形式之间进行转换而不会造成损失的意义上来说,两者是等效的。在NFC下比较两个字符串将始终得到与在NFD下比较两个字符串相同的结果。

无损检测

NFD的字符已完全扩展。这是计算速度更快的规范化形式,但是会导致更多的代码点(即,使用更多的空间)。

如果只想比较尚未标准化的两个字符串,则这是首选的标准化形式,除非您知道需要兼容性标准化。

NFC

运行NFD算法后,NFC会在可能的情况下重组代码点。这会花费更长的时间,但会导致字符串更短。

兼容性归一化

Unicode还包括许多实际上不属于但在旧式字符集中使用的字符。Unicode添加了这些功能,以允许将这些字符集中的文本作为Unicode处理,然后无损地转换回去。

兼容性规范化将它们转换为相应的“真实”字符序列,并执行规范规范化。兼容性归一化的结果可能与原始结果不一样。

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

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

何时使用

Unicode联合会建议将兼容性规范化视为一种ToUpperCase转换。这在某些情况下可能有用,但您不应该随便使用它。

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

您可能不应该做的一件事是显示对用户应用兼容性标准化的结果。

NFKC / NFKD

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

NFKC中的任何字符串本质上也是NFC中的,对于NFKD和NFD也是相同的。因此NFKD(x)=NFD(NFKC(x)),和NFKC(x)=NFC(NFKD(x))等。

结论

如有疑问,请进行规范化归一化处理。根据适用的空间/速度权衡,或根据互操作对象的要求选择NFC或NFD。


42
可以快速记住缩略词的含义:NF = 标准化形式 D = 分解(解压缩)C = 构成(压缩) K = 兼容性(因为使用了“ C”)。
Mike Spross

12
您始终希望将输入中的所有字符串作为首位进行NFD操作,而将NFC所有输出的字符串作为最后一件事。这是众所周知的。
tchrist 2011年

3
@tchrist:这是一个很好的建议,除非在极少数情况下,当不进行任何更改时,您希望输出的字节与输入的字节相同。在某些其他情况下,您需要在内存中使用NFC或在磁盘上使用NFD,但这是例外,而不是规则。
凯文·卡斯卡特

@Kevin:是的,NFD输入和NFC输出将破坏单例。我不确定是否有人在乎这些,但有可能。
tchrist 2011年

2
您可能会认为,但是从附件中可以得出:“要将Unicode字符串转换为给定的Unicode规范化形式,第一步是完全分解字符串”。因此,即使运行NFC,Q-Caron也会首先成为Q + Caron,并且无法重新组成,因为稳定性规则禁止添加新的成分映射。NFC有效定义为NFC(x)=Recompose(NFD(x))
凯文·卡斯卡特

40

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

对于可以由多个基本字符序列和组合标记表示的字符(例如,“ s,在下面的点,在上面的点”与将点放在上面然后在点下或使用已经具有一个点的基本字符),NFD将还要选择其中一个(发生的情况如下)

兼容性分解包括许多“本不应该”为字符的字符,但是因为它们是在旧式编码中使用的。普通规范化不会统一这些规范(以保持往返完整性-这对于合并表单来说不是问题,因为没有传统编码(少数越南编码除外)都使用这两种编码),但是兼容性规范化却可以。就像出现在某些东亚编码(或半角/全角片假名和字母)中的“ kg”公斤符号,或MacRoman中的“ fi”连字一样。

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


1
这确实是正确的答案。如果仅对源自某些旧字符集的文本使用规范化规范化,则结果可以无损地转换回该字符集。如果使用兼容性分解,则最终将没有任何兼容字符,但是不再可能转换回原始字符集而不会丢失。
凯文·卡斯卡特

13

普通格式(Unicode,而不是数据库)主要(唯一地?)处理带有变音标记的字符。Unicode提供了一些带有“内置”变音标记的字符,例如U + 00C0,“带有Grave的拉丁大写字母A”。可以从“拉丁大写字母A”(U + 0041)和“合并重音符号”(U + 0300)创建相同的字符,这意味着即使两个序列产生相同的结果字符,也要逐字节比较将显示它们完全不同。

规范化是对此的一种尝试。规范化确保(或至少尝试)以相同的方式编码所有字符-要么在需要时全部使用单独的组合变音标记,要么在可能的情况下全部使用单个代码点。从比较的角度来看,选择的内容并不重要,几乎所有标准化字符串都可以与另一个标准化字符串进行正确比较。

在这种情况下,“兼容性”是指与假定一个代码点等于一个字符的代码的兼容性。如果您有这样的代码,则可能要使用兼容性标准格式。尽管我从未见过它直接说明过,但正常形式的名称暗示着Unicode联盟认为最好使用单独的组合变音标记。这需要更多的智能来计算字符串中的实际字符(以及智能地断开字符串之类的事情),但是用途更多。

如果您充分利用ICU,则很有可能要使用规范的标准格式。如果您尝试自己编写代码(例如,假设一个代码点等于一个字符),那么您可能希望使用兼容性规范形式,以尽可能多地实现这一点。


因此,这是字素功能出现的部分。不仅字符比ASCII的字节更多-而且多个序列可以是单个字符,对吗?(与MB字符串函数相对。)
Xeoncross

4
不,“一个代码点是一个字符”大致对应于NFC(带有组合标记的一个是NFD,而两者都不是“兼容性”)-兼容性规范化NFKC / NFKD是一个不同的问题;兼容性(或缺乏兼容性),例如对希腊mu和'micro'具有单独字符的传统编码(这很有趣,因为“ compatibility”版本是拉丁1块中的版本)
Random832

@ Random832:糟糕,非常正确。当我过去一两年没有使用它时,我应该比从记忆中消失更好。
杰里·科芬

@ Random832那是不正确的。您的“大约”太在那里了。考虑两个字素ō̲̃和ȭ̲。每种写方法有很多,其中每种恰好是NFC和一种NFD,但也存在其他方法。并非只有一个代码点。NFD是第一个"o\x{332}\x{303}\x{304}",而NFC是"\x{22D}\x{332}"。对于第二个NFD是"o\x{332}\x{304}\x{303}"和NFC是"\x{14D}\x{332}\x{303}"。但是,存在许多非规范的可能性,这些可能性在规范上等同于这些可能性。归一化允许规范等效的字素的二进制比较。
tchrist 2011年

5

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

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

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

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


5

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

字符“Ç”可以表示为字节序列0xc387。但是它也可以由C(0x43)表示,后跟字节序列0xcca7。因此,可以说0xc387和0x43cca7是同一字符。起作用的原因是0xcca7是一个组合标记。也就是说,它需要一个字符(在C此处为a )并对其进行修改。

现在,至于规范对等与兼容性对等之间的差异,我们通常需要看一下字符。

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

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


@tchrist:重新阅读答案。我什至从未提到过代表同一代码点的不同方法。我说过有多种方式来表示相同的印刷字符(通过组合符和多个字符)。这适用于UTF-8和Unicode。因此,您的反对意见和评论根本不适用于我所说的内容。实际上,我基本上是和这里的最高海报指出的观点相同(尽管不是很好)……
ircmaxell 2011年

4

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

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

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


1

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

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

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

W3C和其他(例如文件名)建议使用“按规范构成”(请记住C是“最紧凑”的较短字符串)...因此,

标准是C!怀疑使用NFC

为了实现互操作性以及“通过配置进行约定”,建议使用NFC来“规范化”外部字符串。例如,要存储规范的XML,请将其存储在“ FORM_C”中。Web工作组上的 W3C CSV也推荐了NFC(第7.2节)。

PS:de“ FORM_C”是大多数库中的默认格式。例如 在PHP的normalizer.isnormalized()中


疗法术语“ compostion表”( FORM_C)用于这两个,说“这样的字符串是在C-规范表”(一NFC转换的结果),并说一个变换算法用于...查看HTTP: //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实体引用)的规范化,可以使用此测试/规范化在线转换器


我很困惑。我去了这个在线测试仪页面,然后输入:“TÖSTMÉpleasé”。并尝试所有4种给定的规范化-不会改变我的文本,好吧,除了它会改变用于显示这些字符的代码。我是否在错误地认为“规范化”意味着“删除所有变音符号和类似符号”,而实际上意味着-只是更改下面的utf编码?
userfuser

您好@userfuser也许您需要一个有关应用程序的职位:是比较还是标准化您的文本?我在这里的帖子仅关于“标准化”应用程序。PS:当全世界都在使用标准时,比较问题就消失了。
彼得·克劳斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.