是UTF8内射映射
Is UTF8 injective mapping?
我们编写了一个C++
应用程序,需要知道:
UTF8
文本编码是从字节到字符的内射映射,这意味着每个字符(字母…)仅以一种方式编码?因此,例如字母'Ž'不能同时编码为3231和32119。
这在很大程度上取决于你对"字母"的看法。
UTF8是Unicode的一小部分。
基本上至少有三个层次:字节、代码点和字形簇。一个码点可以被编码成一个或多个字节,根据一定的编码,如UTF8, UTF16或UTF32。这种编码是唯一的(因为所有替代方法都被声明为无效)。然而,代码点并不总是字形,因为有所谓的组合字符。这种组合字符跟在基本字符后面,顾名思义,与基本字符组合。例如,有一个组合字符U+0308 combined DIAERESIS,它在前面的字母上面放了一个DIAERESIS(¨)。因此,如果它跟在例如a (U+0061 LATIN SMALL LETTER a)后面,则结果是ä。然而,字母ä也有一个单一的代码点(U+00E4拉丁小写字母a WITH DIAERESIS),所以这意味着代码序列U+0061 U+0308和U+00E4描述了同一个字母。
因此,每个码点都有一个有效的UTF8编码(例如U+0061是"141",U+0308是"314210",U+00e4是"303244"),但是字母ä由码点序列U+0061 U+0308编码,即在UTF8中字节序列"141314210"和单个码点U+00e4编码,即字节序列"303244"。
更糟糕的是,由于Unicode制定者决定组合字母在基础字母之后,而不是在它之前,所以在看到下一个代码点之前,您无法知道您的字形是否完整(如果它不是组合代码点,那么您的字母就完成了)。
Valid UTF-8确实对每个字符进行了唯一编码。然而,也存在所谓的过长序列,它们符合一般的编码方案,但根据定义是无效的,因为只有最短的序列才能用于编码字符。
例如,有一个UTF-8的衍生物称为修改的 UTF-8,它将NUL编码为过长序列0xC0 0x80
而不是0x00
,以获得与以空结尾的字符串兼容的编码。
如果你问的是字素簇(即用户感知的字符)而不是字符,那么即使是有效的UTF-8也是模棱两可的。但是,Unicode定义了几种不同的规范化形式,如果您将自己限制为规范化字符串,那么UTF-8确实是内射的。
有点离题:这里是一些我想出的ASCII艺术,以帮助可视化字符的不同概念。垂直分开的是人, 抽象和机器级别。请随便想出更好的名字…
[user-perceived characters]<-+
^ |
| |
v |
[characters] <-> [grapheme clusters] |
^ ^ |
| | |
v v |
[bytes] <-> [codepoints] [glyphs]<----------+
回到主题:此图还显示了当使用字节比较抽象字符串时可能出现的问题。特别是(假设是UTF-8),程序员需要确保
- 字节序列是有效的,即不包含过长的序列或编码非字符码点
- 字符序列是标准化的,所以等价的字素簇有一个唯一的表示
首先你需要一些术语:
- 字母:(抽象概念,非Unicode)您想要表示的某个字母或符号。
- 代码点:与Unicode字符相关联的数字。
-
Grapheme cluster:对应于单个字母的Unicode码点序列,例如:
a
+́
代表字母á
。 - 字形:(字体级别的概念,而不是Unicode):字母的图形表示。
每个码点(例如:U+1F4A9)在UTF-8中获得唯一的字节表示(例如:0xF0 0x9F 0x92 0xA9)。
一些字母可以用几种不同的方式表示为码点(即:作为不同的字素簇)。例如:á
可以表示为单个码点á
(拉丁小写字母a WITH ACUTE),也可以表示为a
(拉丁小写字母a) + ́
(组合急性重音)的码点。Unicode有几种规范规范化形式来处理这个问题(例如:NFC或规范规范化形式C是一种具有较少代码点的松散规范化形式,而NFD是完全分解的)。
然后,还有一些字母的结合力(例如:fi
)和其他一些与表示相关的字母变体(例如:上标,无间断空格,单词不同位置的不同形状的字母,…)。其中一些是Unicode的,允许从旧字符集到旧字符集的无损往返转换。Unicode有兼容性规范化形式(NFKC和NFKD)来处理这个问题。
是。UTF-8只是编码Unicode字符的标准方法。这样一来,每个Unicode字符只有一种编码方式。
有点离题了:知道一些字符在外观上非常相似(与人类)可能是有用的,但它们仍然不同-例如,在西里尔字母中有一个符号看起来非常类似于'/'。
是的,有点像。如果使用得当,每个unicode码点在UTF-8中应该只以一种方式编码,但这在一定程度上是因为任何字符都应该只使用最短的适用UTF-8字节序列。
然而,如果不满足此要求,用于编码字符的方法可以以多种方式编码许多字符——尽管不合适,但在某些情况下可以这样做。例如,'Z'可以编码为0x5a
或{0xa1, 0x9a}
(以及其他),尽管唯一的0x5a
被认为是正确的,因为它是最短的序列。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 将函数类成员映射到类本身内部
- 如何在 C# 中映射双 C 结构指针?
- 如何在C++中使用结构生成映射
- 使用std::函数映射对象方法
- 如何加载(或映射)文件部分的最大大小,但适合在Windows上的RAM
- C++映射分割错误(核心转储)
- 内联映射初始化的动态atexit析构函数崩溃
- 使用"std::unordereded_map"映射到"std::list"对象
- 如何从多映射中删除特定的重复项
- 在未初始化映射的情况下,将值插入到映射的映射中
- QT通过C++添加映射QML项目
- 在c++中访问int到类对象的映射时出错
- 在C++中搜索嵌套多映射值
- 错误处理.将系统错误代码映射到泛型
- C++匿名结构作为std::映射值
- 如何从存储在std::映射中的std::集中删除元素
- 递归无序映射
- 是UTF8内射映射