UTF-8、UTF-16、UTF-32

UTF-8、UTF-16和UTF-32之间有什么区别?

我知道它们都将存储Unicode,并且每个都使用不同的字节数来表示一个字符。选择一个比另一个有优势吗?

286694 次浏览

简而言之:

  • 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:固定宽度编码。所有代码点占用4个字节。占用大量内存,但操作速度很快。很少使用。

长:参见维基百科:utf - 8utf - 16utf - 32

  • 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

UTF-8在ASCII字符代表文本块中的大部分字符的情况下具有优势,因为UTF-8将这些字符编码为8位(像ASCII一样)。它的另一个优点是只包含ASCII字符的UTF-8文件具有与ASCII文件相同的编码。

在ASCII不占主导地位的情况下,UTF-16更好,因为它主要每个字符使用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字符,您会浪费额外的3个字节。

在UTF-8字符有可变长度,ASCII字符编码为一个字节(8位),大多数西方特殊字符编码为两个字节或三个字节(例如€是三个字节),更奇特的字符可以占用四个字节。明显的缺点是,先验你不能计算字符串的长度。但与UTF-32相比,编码拉丁(英语)字母文本所需的字节要少得多。

UTF-16也是可变长度的。字符编码为两个字节或四个字节。我真的不明白这有什么意义。它有可变长度的缺点,但没有像UTF-8那样节省空间的优点。

在这三种语言中,UTF-8显然是传播最广泛的。

我做了一些测试来比较MySQL中UTF-8和UTF-16之间的数据库性能。

更新的速度

utf - 8

Enter image description here

utf - 16

Enter image description here

插入的速度

Enter image description here

Enter image description here

删除速度

Enter image description here

Enter image description here

Unicode是一个标准,关于UTF-x,你可以认为是一个技术实现,出于一些实际目的:

  • utf - 8 - "尺寸优化":最适合基于拉丁字符的数据(或ASCII),每个字符只需要1个字节,但大小会相应增加符号的种类(在最坏的情况下,每个字符可以增长到6个字节)
  • utf - 16 - "平衡":每个字符至少需要2个字节,这对于现有的主流语言来说已经足够了,它有固定的大小,以方便字符处理(但大小仍然是可变的,可以增长到每个字符4个字节)
  • utf - 32 -“性能”:由于字符大小固定(4字节),允许使用简单的算法,但内存不足

utf - 8

  • 没有字节顺序的概念
  • 每个字符使用1到4个字节
  • ASCII是一种兼容的编码子集
  • 完全自同步,例如从流中的任何地方删除字节最多只会损坏一个字符
  • 几乎所有的欧洲语言都是用两个字节或更少的字符编码的

utf - 16

  • 必须使用已知的字节顺序进行解析,或者读取字节顺序标记(BOM)。
  • 每个字符使用2或4个字节

utf - 32

  • 每个字符是4个字节
  • 必须使用已知的字节顺序进行解析,或者读取字节顺序标记(BOM)。

UTF-8将是空间效率最高的,除非大多数字符来自CJK(中国、日本和韩国)字符空间。

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 和# EYZ0 < / p >

我试图在我的博客中给出一个简单的解释。

utf - 32

需要32位(4字节)来编码任何字符。例如,为了使用这个方案来表示“A”字符代码点,你需要用32位二进制数字写65:

00000000 00000000 00000000 01000001 (Big Endian)

如果仔细观察,您会注意到最右边的7位实际上是使用ASCII格式时的相同位。但是由于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-8的首要优势。

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"然后每隔3个代码点进行拆分

ABC
DEF
GHI

这是错误的。# EYZ0。例如颜色选择器代码点,让你选择👨🏻‍🦳👨🏼‍🦳👨🏽‍🦳👨🏾‍🦳👨🏿‍🦳。如果你在任意的代码点上分裂,你就会破坏它们。

另一个例子是双向码点。下面的段落是反向输入的。它的前面是0x202E码点

  • 这一行不是向后输入的,而是向后显示的

因此,utf-32不允许您不考虑unicode字符串的含义而随意操作它们。它将允许您查看每个代码点,而不需要额外的代码。

不过供您参考,utf-8的设计是为了查看任何单独的字节,您都可以找到当前代码点或下一个代码点的开始。

如果取utf-8数据中的任意一个字节。如果是<128它本身就是正确的码位。如果是>= 128 and <192(前两位是10),然后要找到编码点的开始,您需要查找前面的字节,直到找到值为>= 192的字节(前两位是11)。在那个字节上,您已经找到了代码点的开始。该字节编码有多少后续字节使该代码指向。

如果你想找到下一个码点只要扫描到字节<128或>= 192,这是下一个码位的开始。

Num字节 第一个编码点 最后一个编码点 1字节 2字节 3字节 4字节
1 # EYZ0 # EYZ0 # EYZ0
2 # EYZ0 # EYZ0 # EYZ0 # EYZ0
3. # EYZ0 # EYZ0 # EYZ0 # EYZ0 # EYZ0
4 # EYZ0 # EYZ0 # EYZ0 # EYZ0 # EYZ0 # EYZ0

其中xxxxxx是代码点的位。连接字节中的xxxx位以获得代码点

在阅读完答案后,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秒-大小2.5亿

UTF-32—消失3.143秒-大小125,030,570

ASCII—经过2.362秒-大小500,000,000

Utf-32——丢麦克风