提问人: 提问时间:1/31/2009 最后编辑:Peter Mortensen 更新时间:5/11/2023 访问量:317391
UTF-8、UTF-16 和 UTF-32
UTF-8, UTF-16, and UTF-32
问:
UTF-8、UTF-16 和 UTF-32 之间有什么区别?
我知道它们都将存储Unicode,并且每个都使用不同数量的字节来表示一个字符。选择一个比另一个有优势吗?
答:
总之:
- UTF-8:可变宽度编码,向后兼容 ASCII。ASCII 字符(U+0000 到 U+007F)需要 1 个字节,代码点 U+0080 到 U+07FF 需要 2 个字节,代码点 U+0800 到 U+FFFF 需要 3 个字节,代码点 U+10000 到 U+10FFFF 需要 4 个字节。适合英文文本,不太适合亚洲文本。
- UTF-16:可变宽度编码。代码点 U+0000 到 U+FFFF 需要 2 个字节,代码点 U+10000 到 U+10FFFF 需要 4 个字节。对英语文本不利,对亚洲文本有利。
- UTF-32:固定宽度编码。所有代码点都需要四个字节。一个巨大的内存消耗,但操作速度很快。很少使用。
长篇:参见维基百科:UTF-8、UTF-16 和 UTF-32。
评论
wchar_t
-fshort-wchar
UTF-8 是 1 到 4 个字节的变量。
UTF-16 是变量 2 或 4 个字节。
UTF-32 固定为 4 个字节。
评论
当 ASCII 字符表示文本块中的大多数字符时,UTF-8 具有优势,因为 UTF-8 将这些字符编码为 8 位(如 ASCII)。它还具有优势,因为仅包含 ASCII 字符的 UTF-8 文件具有与 ASCII 文件相同的编码。
UTF-16 在 ASCII 不占主导地位的情况下更好,因为它主要使用每个字符 2 个字节。UTF-8 将开始对高阶字符使用 3 个或更多字节,而大多数字符的 UTF-16 仅保留 2 个字节。
UTF-32 将在 4 个字节中涵盖所有可能的字符。这使得它非常臃肿。我想不出使用它有什么好处。
评论
如前所述,差异主要在于基础变量的大小,在每种情况下,基础变量都会变大以允许表示更多字符。
然而,字体、编码和东西都非常复杂(不必要?),所以需要一个大链接来填写更多细节:
http://www.cs.tut.fi/~jkorpela/chars.html#ascii
不要指望能理解这一切,但如果你不想以后出现问题,那么值得尽可能多地学习,尽早(或者只是让别人为你解决)。
保罗。
评论
Unicode 定义了一个巨大的字符集,为每个图形符号分配一个唯一的整数值(这是一个重大的简化,实际上不是真的,但对于这个问题来说,它已经足够接近了)。UTF-8/16/32 只是编码的不同方法。
简而言之,UTF-32 对每个字符使用 32 位值。这允许他们为每个字符使用固定宽度的代码。
UTF-16 默认使用 16 位,但这只能为您提供 65k 个可能的字符,这对于完整的 Unicode 集来说远远不够。因此,某些字符使用 16 位值对。
而 UTF-8 默认使用 8 位值,这意味着前 127 个值是固定宽度的单字节字符(最高有效位用于表示这是多字节序列的开始,剩下 7 位作为实际字符值)。所有其他字符都编码为最多 4 个字节的序列(如果内存可用)。
这为我们带来了优势。任何 ASCII 字符都直接与 UTF-8 兼容,因此对于升级旧版应用程序,UTF-8 是一个常见且显而易见的选择。在几乎所有情况下,它也会使用最少的内存。另一方面,您无法对字符的宽度做出任何保证。它的宽度可能为 1、2、3 或 4 个字符,这使得字符串操作变得困难。
UTF-32 则相反,它使用最多的内存(每个字符的宽度是固定的 4 个字节),但另一方面,你知道每个字符都有这个精确的长度,所以字符串操作变得简单得多。您可以简单地根据字符串的长度(以字节为单位)来计算字符串中的字符数。你不能用 UTF-8 做到这一点。
UTF-16 是一种折衷方案。它允许大多数字符适合固定宽度的 16 位值。因此,只要您没有中文符号、音符或其他一些符号,您就可以假设每个字符的宽度为 16 位。它使用的内存比 UTF-32 少。但在某些方面,这是“两全其美”的。它几乎总是比 UTF-8 使用更多的内存,并且仍然无法避免困扰 UTF-8(可变长度字符)的问题。
最后,只使用平台支持的内容通常会有所帮助。Windows 在内部使用 UTF-16,因此在 Windows 上,这是显而易见的选择。
Linux 略有不同,但它们通常将 UTF-8 用于符合 Unicode 的所有内容。
所以简短的回答:所有三种编码都可以编码相同的字符集,但它们将每个字符表示为不同的字节序列。
评论
根据您的开发环境,您甚至可能无法选择字符串数据类型在内部使用的编码方式。
但是对于存储和交换数据,如果您可以选择,我总是使用 UTF-8。如果您主要拥有 ASCII 数据,这将为您提供最少的数据传输量,同时仍然能够对所有内容进行编码。优化最少的 I/O 是现代机器的必经之路。
评论
在 UTF-32 中,所有字符都用 32 位编码。优点是您可以轻松计算字符串的长度。缺点是,对于每个 ASCII 字符,您会浪费额外的三个字节。
在 UTF-8 字符中,字符的长度可变,ASCII 字符以一个字节(八位)编码,大多数西方特殊字符以两个字节或三个字节编码(例如 € 是三个字节),而更多外来字符最多可以占用四个字节。明显的缺点是,先验地无法计算字符串的长度。但与 UTF-32 相比,编码拉丁(英语)字母文本所需的字节要少得多。
UTF-16 也是可变长度的。字符以 2 个字节或 4 个字节编码。我真的不明白这一点。它的缺点是长度可变,但没有像 UTF-8 那样节省那么多空间的优点。
在这三者中,UTF-8显然是传播最广泛的。
评论
我做了一些测试来比较 MySQL 中 UTF-8 和 UTF-16 之间的数据库性能。
更新速度
UTF-8 格式
UTF-16 格式
插入速度
删除速度
评论
Unicode是一个标准,关于UTF-x,你可以把它看作是一些实际目的的技术实现:
- UTF-8 - “大小优化”:最适合基于拉丁字符的数据(或 ASCII),每个字符只需要 1 个字节,但大小会相应地增长符号种类(在最坏的情况下,每个字符最多可以增长 6 个字节)
- UTF-16 - “balance”:每个字符至少需要 2 个字节,这对于现有的主流语言集来说已经足够了,并且具有固定大小以简化字符处理(但大小仍然是可变的,每个字符最多可以增长 4 个字节)
- UTF-32 - “性能”:允许使用简单的算法作为固定大小字符(4 个字节)的结果,但内存缺点
评论
UTF-8 格式
- 没有字节顺序的概念
- 每个字符使用 1 到 4 个字节
- ASCII 是编码的兼容子集
- 完全自同步,例如,从流中任何位置删除的字节最多会损坏一个字符
- 几乎所有的欧洲语言都以每个字符两个字节或更少的字节编码
UTF-16 格式
- 必须使用已知字节顺序或读取字节顺序标记 (BOM) 进行解析
- 每个字符使用 2 或 4 个字节
UTF-32 格式
- 每个字符是 4 个字节
- 必须使用已知字节顺序或读取字节顺序标记 (BOM) 进行解析
UTF-8 将是最节省空间的,除非大多数字符来自 CJK(中文、日文和韩文)字符空间。
UTF-32 最适合通过字符偏移到字节数组中进行随机访问。
评论
0xxxxxxx
110xxxxx
10xxxxxx
10xxxxxx
110xxxxxx
简而言之,使用 UTF-16 或 UTF-32 的唯一原因是分别支持非英语和古代脚本。
我想知道为什么有人会选择使用非 UTF-8 编码,而它显然对 Web/编程目的更有效。
一个常见的误解 - 后缀数字并不表示其能力。它们都支持完整的 Unicode,只是 UTF-8 可以用一个字节处理 ASCII,因此对 CPU 和互联网的效率更高/更不易损坏。
一些好读物:http://www.personal.psu.edu/ejp10/blogs/gotunicode/2007/10/which_utf_do_i_use.html 和 http://utf8everywhere.org
评论
我试图在我的博文中给出一个简单的解释。
UTF-32 格式
需要 32 位(4 个字节)来编码任何字符。例如,为了使用此方案表示“A”字符代码点,您需要用 32 位二进制数写入 65:
00000000 00000000 00000000 01000001 (Big Endian)
如果你仔细观察,你会注意到,在使用 ASCII 方案时,最右边的 7 位实际上是相同的位。但是由于 UTF-32 是固定宽度方案,我们必须附加三个额外的字节。这意味着,如果我们有两个只包含“A”字符的文件,一个是 ASCII 编码的,另一个是 UTF-32 编码的,它们的大小将对应为 1 个字节和 4 个字节。
UTF-16 格式
许多人认为,由于 UTF-32 使用固定宽度的 32 位来表示代码点,因此 UTF-16 是固定宽度的 16 位。错!
在 UTF-16 中,代码点可能以 16 位或 32 位表示。所以这个方案是可变长度编码系统。与 UTF-32 相比有什么优势?至少对于 ASCII,文件大小不会是原始文件的 4 倍(但仍然是两倍),因此我们仍然不向后兼容 ASCII。
由于 7 位足以表示“A”字符,我们现在可以使用 2 个字节而不是像 UTF-32 那样的 4 个字节。它看起来像:
00000000 01000001
UTF-8 格式
你猜对了..在 UTF-8 中,码位可能用 32、16、24 或 8 位表示,作为 UTF-16 系统,这也是可变长度编码系统。
最后,我们可以用与使用 ASCII 编码系统相同的方式表示“A”:
01001101
一个小例子,UTF-16 实际上比 UTF-8 更好:
考虑中文字母“語”——它的 UTF-8 编码是:
11101000 10101010 10011110
虽然它的 UTF-16 编码较短:
10001010 10011110
为了了解表示形式及其解释方式,请访问原始帖子。
评论
我很惊讶这个问题已经有 11 年的历史了,而且没有一个答案提到 utf-1 的 #8 优势。
UTF-8 通常适用于无法识别 UTF-8 的程序。这在一定程度上是它的设计目的。其他答案提到前 128 个代码点与 ASCII 相同。所有其他代码点都由设置了高位(值从 128 到 255)的 8 位值生成,因此从非 unicode 感知程序的 POV 中,它只会将字符串视为带有一些额外字符的 ASCII。
举个例子,假设你编写了一个程序来添加行号,可以有效地做到这一点(为了简单起见,让我们假设行尾只是 ASCII 13)
// pseudo code
function readLine
if end of file
return null
read bytes (8bit values) into string until you hit 13 or end or file
return string
function main
lineNo = 1
do {
s = readLine
if (s == null) break;
print lineNo++, s
}
将 utf-8 文件传递给此程序将继续工作。同样,在制表符、逗号上拆分、解析 ASCII 引号或其他只有 ASCII 值很重要的解析都仅适用于 utf-8,因为 utf-8 中没有 ASCII 值,除非它们实际上是这些 ASCII 值
其他一些答案或评论提到 utf-32 的优点是您可以单独处理每个代码点。例如,这建议您可以采用像“ABCDEFGHI”这样的字符串,并在每 3 个代码点将其拆分
ABC
DEF
GHI
这是错误的。许多代码点会影响其他代码点。例如,颜色选择器代码点允许您在 👨🏻 🦳👨🏿 🦳👨🏼 🦳👨🏾 🦳👨🏽 🦳 .如果你在任何任意代码点拆分,你就会破坏它们。
另一个例子是双向码位。以下段落没有倒过来。它前面是0x202E代码点
- This line is not typed backward it is only displayed backward
所以不,utf-32 不会让您随意操作 unicode 字符串而不考虑它们的含义。它可以让您在没有额外代码的情况下查看每个代码点。
仅供参考,utf-8 的设计使得查看任何单个字节,您都可以找出当前代码点或下一个代码点的开始。
如果在 utf-8 数据中采用任意字节。如果它< 128,则它本身就是正确的代码点。如果它是 >= 128 且< 192(前 2 位是 10),那么要找到代码点的开头,您需要查看前面的字节,直到找到值为 >= 192 的字节(前 2 位是 11)。在那个字节处,你已经找到了代码点的起点。该字节对构成代码点的后续字节数进行编码。
如果要查找下一个代码点,只需扫描直到字节< 128 或 >= 192,这就是下一个代码点的开始。
字节数 | 第一个代码点 | 最后一个代码点 | 字节 1 | 字节 2 | 字节 3 | 字节 4 |
---|---|---|---|---|---|---|
1 | U+0000 |
U+007F |
0xxxxxxx |
|||
2 | U+0080 |
U+07FF |
110xxxxx |
10xxxxxx |
||
3 | U+0800 |
U+FFFF |
1110xxxx |
10xxxxxx |
10xxxxxx |
|
4 | U+10000 |
U+10FFFF |
11110xxx |
10xxxxxx |
10xxxxxx |
10xxxxxx |
代码点的位在哪里。将字节中的 xxxx 位连接起来以获取代码点xxxxxx
评论
看完答案后,UTF-32 需要一些爱。
C#:
Data1 = RandomNumberGenerator.GetBytes(500_000_000);
sw = Stopwatch.StartNew();
int l = Encoding.UTF8.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"UTF-8: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s} Size - {l:###,###,###}");
sw = Stopwatch.StartNew();
l = Encoding.Unicode.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"Unicode: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s} Size - {l:###,###,###}");
sw = Stopwatch.StartNew();
l = Encoding.UTF32.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"UTF-32: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s} Size - {l:###,###,###}");
sw = Stopwatch.StartNew();
l = Encoding.ASCII.GetString(Data1).Length;
sw.Stop();
Console.WriteLine($"ASCII: Elapsed - {sw.ElapsedMilliseconds * .001:0.000s} Size - {l:###,###,###}");
UTF-8 -- 已用 9.939 秒 - 大小 473,752,800
Unicode -- 经过 0.853s - 大小 250,000,000
UTF-32 -- 经过 3.143 秒 - 大小 125,030,570
ASCII -- 经过 2.362 秒 - 大小 500,000,000
UTF-32 -- 麦克风丢弃
评论