在 Ubuntu 18.04 上,我创建了一个仅包含一个 UTF-8 字符的虚拟文本文件è
.其他角色全部是ascii
:
$ cat dummytext
Hello
Helloè
这是结果hexdump
:
$ hexdump -C dummyfile
00000000 48 65 6c 6c 6f 0a 48 65 6c 6c 6f c3 a8 0a |Hello.Hello...|
0000000e
该文件被标识为
$ file dummyfile
dummyfile2: UTF-8 Unicode text
每个字符都由一个表示单身的字节,除了UTF-8è
字符,即c3a8
,所以用2个字节表示。如果用于表示每个字符的字节数不是恒定的,如何正确解释文件内容?
我的猜测:也许解析器在遇到大于最后一个ascii
字符的十六进制值时7F
(这是 的情况c3
),被迫读取至少另一个字节,以确定要打印的正确字符?
答案1
BSD 手册第 5 节中有关 UTF8 的页面内容如下:
描述
UTF-8 编码将 UCS-4 字符表示为八位位组序列,每个字符使用 1 到 6 之间的数字。它向后兼容 ASCII,因此
0x00
-0x7f
请参阅 ASCII 字符集。非 ASCII 字符的多字节编码完全由设置了高位的字节组成。实际的编码如下表所示:
[0x00000000 - 0x0000007f] [00000000.0bbbbbbb] -> 0bbbbbbb [0x00000080 - 0x000007ff] [00000bbb.bbbbbbbb] -> 110bbbbb, 10bbbbbb [0x00000800 - 0x0000ffff] [bbbbbbbb.bbbbbbbb] -> 1110bbbb, 10bbbbbb, 10bbbbbb [0x00010000 - 0x001fffff] [00000000.000bbbbb.bbbbbbbb.bbbbbbbb] -> 11110bbb, 10bbbbbb, 10bbbbbb, 10bbbbbb [0x00200000 - 0x03ffffff] [000000bb.bbbbbbbb.bbbbbbbb.bbbbbbbb] -> 111110bb, 10bbbbbb, 10bbbbbb, 10bbbbbb, 10bbbbbb [0x04000000 - 0x7fffffff] [0bbbbbbb.bbbbbbbb.bbbbbbbb.bbbbbbbb] -> 1111110b, 10bbbbbb, 10bbbbbb, 10bbbbbb, 10bbbbbb, 10bbbbbb
如果存在多个值的表示形式(例如,
0x00
;0xC0 0x80
;0xE0 0x80 0x80
),则始终使用最短的表示形式。较长的字符会被检测为错误,因为它们会带来潜在的安全风险,并会破坏 1:1 字符:八位字节序列映射。
Linux 手册第 7 节中有关 UTF8 的页面类似地写道:
描述
[... UTF-8 在某些情况下比 UCS-2 更好,部分原因是,大多数 UNIX 工具都需要 ASCII 文件,并且在没有重大修改的情况下无法将 16 位字读取为字符。 [...]
Unicode 和 UCS 的 UTF-8 编码不存在这些问题,并且是 UNIX 风格的操作系统上使用 Unicode 的常用方式。
特性
UTF-8 编码具有以下优良特性:
- UCS 字符
0x00000000
(0x0000007f
经典的 US-ASCII 字符)简单地编码为字节0x00
(0x7f
ASCII 兼容性)。这意味着仅包含 7 位 ASCII 字符的文件和字符串在 ASCII 和 UTF-8 下具有相同的编码。
所以实际上不可能区分 ASCII 和 UTF-8,因为在 UTF-8 文件中,ASCII是UTF-8。 file
查看文件的前 96KiB 并尝试确定它是什么。由于它看到多个 UTF-8 代码序列,因此它确定该文件为 UTF-8,因为它是 ASCII 的严格超集。