UTF-8、UTF-16 和 UTF-32 之间有什么区别?
我知道它们都将存储 Unicode,并且每个都使用不同数量的字节来表示一个字符。选择一个比另一个有优势吗?
UTF-8 在 ASCII 字符代表文本块中的大多数字符的情况下具有优势,因为 UTF-8 将这些字符编码为 8 位(如 ASCII)。另一个优点是仅包含 ASCII 字符的 UTF-8 文件与 ASCII 文件具有相同的编码。
UTF-16 在 ASCII 不占优势的情况下更好,因为它主要使用每个字符 2 个字节。UTF-8 将开始为高阶字符使用 3 个或更多字节,而 UTF-16 对大多数字符仅保留 2 个字节。
UTF-32 将覆盖 4 个字节中所有可能的字符。这使它非常臃肿。我想不出使用它有什么好处。
简而言之:
UTF-8 是可变的1 到 4个字节。
UTF-16 是可变的2 或 4个字节。
UTF-32 固定为4个字节。
注意:UTF-8 可以采用 1 到 6 个字节的最新约定:https ://lists.gnu.org/archive/html/help-flex/2005-01/msg00030.html
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 略有不同,但它们通常对所有符合 Unicode 的内容使用 UTF-8。
如此简短的回答:所有三种编码都可以编码相同的字符集,但它们将每个字符表示为不同的字节序列。
Unicode是一种标准,关于UTF-x,您可以将其视为出于某些实际目的的技术实现:
我试图在我的博文中给出一个简单的解释。
需要 32 位(4 个字节)来编码任何字符。例如,为了使用此方案表示“A”字符代码点,您需要将 65 写入 32 位二进制数:
00000000 00000000 00000000 01000001 (Big Endian)
如果您仔细观察,您会注意到最右边的七位在使用 ASCII 方案时实际上是相同的位。但是由于 UTF-32 是固定宽度方案,我们必须附加三个额外的字节。这意味着如果我们有两个只包含“A”字符的文件,一个是 ASCII 编码的,另一个是 UTF-32 编码的,它们的大小将分别为 1 字节和 4 字节。
很多人认为 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 中,代码点可能使用 32、16、24 或 8 位来表示,并且作为 UTF-16 系统,这也是可变长度编码系统。
最后,我们可以像使用 ASCII 编码系统一样表示“A”:
01001101
考虑中文字母“语” - 它的 UTF-8 编码是:
11101000 10101010 10011110
虽然它的 UTF-16 编码更短:
10001010 10011110
要了解表示形式及其解释方式,请访问原始帖子。
除非大多数字符来自 CJK(中文、日文和韩文)字符空间,否则 UTF-8 将是最节省空间的。
UTF-32 最适合通过字符偏移到字节数组中进行随机访问。
在 UTF-32 中,所有字符都用 32 位编码。优点是可以轻松计算字符串的长度。缺点是对于每个 ASCII 字符,您会浪费额外的三个字节。
在 UTF-8 中字符长度可变,ASCII 字符编码为一个字节(八位),大多数西方特殊字符编码为两个字节或三个字节(例如 € 是三个字节),并且可以占用更多外来字符到四个字节。明显的缺点是,您无法先验地计算字符串的长度。但与 UTF-32 相比,对拉丁(英语)字母文本进行编码所需的字节数要少得多。
UTF-16 也是可变长度的。字符以两个字节或四个字节编码。我真的不明白这一点。它具有可变长度的缺点,但没有像 UTF-8 那样节省空间的优点。
在这三个中,显然 UTF-8 是传播最广泛的。
我做了一些测试来比较 MySQL 中 UTF-8 和 UTF-16 之间的数据库性能。
我很惊讶这个问题已经 11 岁了,而且没有一个答案提到 utf-8 的 #1 优势。
utf-8 通常适用于不支持 utf-8 的程序。这部分是它的设计目的。其他答案提到前 128 个代码点与 ASCII 相同。所有其他代码点均由设置高位的 8 位值(值从 128 到 255)生成,因此从非 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”之类的字符串,并在每个第三个代码点将其拆分为
ABC
DEF
GHI
这是错误的。许多代码点会影响其他代码点。例如,让您在 之间进行选择的颜色选择器代码点。如果您在任意代码点拆分,您将破坏这些代码点。
另一个例子是双向代码点。以下段落没有倒着输入。它前面只是 0x202E 代码点
- 此行未向后键入,仅向后显示
所以不,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 |
xxxxxx
代码点的位在哪里。连接字节中的 xxxx 位以获取代码点
根据您的开发环境,您甚至可能无法选择您的字符串数据类型将在内部使用什么编码。
但是对于存储和交换数据,如果您可以选择,我将始终使用 UTF-8。如果您主要有 ASCII 数据,这将为您提供最少量的数据传输,同时仍然能够对所有内容进行编码。优化最少的 I/O 是现代机器上的方法。
如前所述,区别主要在于基础变量的大小,在每种情况下都会变大以允许表示更多字符。
然而,字体、编码和东西都非常复杂(不必要?),所以需要一个大链接来填写更多细节:
http://www.cs.tut.fi/~jkorpela/chars.html#ascii
不要期望全部理解,但是如果您不想以后遇到问题,那么值得尽可能早地学习(或者只是让其他人为您解决问题)。
保罗。
通读答案后,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.853 秒 - 大小 250,000,000
UTF-32 - 经过 3.143 秒 - 大小 125,030,570
ASCII -- 经过 2.362 秒 - 大小 500,000,000
UTF-32 -- 麦克风丢弃
简而言之,使用 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