146

UTF-8 和 UTF-16 的区别?为什么我们需要这些?

MessageDigest md = MessageDigest.getInstance("SHA-256");
String text = "This is some text";

md.update(text.getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] digest = md.digest();
4

5 回答 5

295

我相信网上有很多关于这方面的好文章,但这里有一个简短的总结。

UTF-8 和 UTF-16 都是可变长度编码。但是,在 UTF-8 中,一个字符可能占用最少 8 位,而在 UTF-16 中,字符长度从 16 位开始。

主要的 UTF-8 优点:

  • 基本的 ASCII 字符,如数字、没有重音的拉丁字符等,占用一个字节,这与 US-ASCII 表示相同。这样,所有 US-ASCII 字符串都变为有效的 UTF-8,这在许多情况下提供了不错的向后兼容性。
  • 没有空字节,允许使用以空字符结尾的字符串,这也引入了大量的向后兼容性。
  • UTF-8 独立于字节顺序,因此您不必担心 Big Endian / Little Endian 问题。

主要的 UTF-8 缺点:

  • 许多常见字符的长度不同,这会减慢按代码点进行索引的速度,并且会严重地计算代码点计数。
  • 即使字节顺序无关紧要,有时 UTF-8 仍然具有 BOM(字节顺序标记),用于通知文本以 UTF-8 编码,并且即使文本仅包含 ASCII 字符也会破坏与 ASCII 软件的兼容性. Microsoft 软件(如记事本)特别喜欢将 BOM 添加到 UTF-8。

主要的 UTF-16 优点:

  • BMP(基本多语言平面)字符,包括拉丁文、西里尔文、大多数中文(PRC 强制支持 BMP 之外的一些代码点),大多数日文可以用 2 个字节表示。如果文本不包含补充字符,这会加快索引和计算代码点计数。
  • 即使文本有补充字符,它们仍然由成对的 16 位值表示,这意味着总长度仍然可以被 2 整除,并且允许使用 16 位char作为字符串的原始组件。

主要的 UTF-16 缺点:

  • US-ASCII 字符串中有很多空字节,这意味着没有以空结尾的字符串和大量浪费的内存。
  • 在许多常见情况下(尤其是在美国/欧盟/使用西里尔字母的国家/以色列/阿拉伯国家/伊朗和许多其他国家)中,将其用作固定长度编码“大部分都有效”,通常会导致支持中断。这意味着程序员必须了解代理对并在重要的情况下正确处理它们!
  • 它是可变长度的,因此计数或索引代码点的成本很高,尽管低于 UTF-8。

一般来说,UTF-16 通常更适合内存中的表示,因为 BE/LE 在那里不相关(只使用本机顺序)并且索引更快(只是不要忘记正确处理代理对)。另一方面,UTF-8 非常适合文本文件和网络协议,因为不存在 BE/LE 问题,并且空终止通常会派上用场,而且兼容 ASCII。

于 2011-01-11T07:50:03.140 回答
20

它们只是表示 Unicode 字符的不同方案。

两者都是可变长度的 - UTF-16 对包含大多数常用字符的基本多语言平面 (BMP) 中的所有字符使用 2 个字节。

UTF-8 对 BMP 中的字符使用 1 到 3 个字节,对于当前 Unicode 范围 U+0000 到 U+1FFFFF 中的字符最多使用 4 个字节,并且如果有必要的话可以扩展到 U+7FFFFFFF...但值得注意的是,所有 ASCII 字符均以单个字节表示。

出于消息摘要的目的,您选择其中的哪一个并不重要,只要尝试重新创建摘要的每个人都使用相同的选项。

有关 UTF-8 和 Unicode 的更多信息,请参阅此页面

(请注意,所有 Java 字符都是 BMP 中的 UTF-16 代码点;要表示 U+FFFF 以上的字符,您需要在 Java 中使用代理对。)

于 2011-01-11T07:41:19.987 回答
8

安全性:仅使用 UTF-8

UTF-8 和 UTF-16 的区别?为什么我们需要这些?

在UTF-16的实现中至少存在几个安全漏洞。有关详细信息,请参阅维基百科

WHATWGW3C现在已经宣布在 Web 上只能使用UTF-8 。

当专门使用 UTF-8 时,此处概述的 [安全] 问题就会消失,这是现在所有事物的强制编码的众多原因之一。

其他团体也这么说。

因此,虽然 UTF-16 可能会继续在 Java 和 Windows 等系统内部使用,但您过去可能在数据文件、数据交换等方面看到的 UTF-16 很少使用,可能会完全消失。

于 2019-05-29T23:22:28.147 回答
4

这与 UTF-8/16 无关(通常,虽然它确实转换为 UTF16 并且 BE/LE 部分可以用单行设置),但下面是将 String 转换为 byte[] 的最快方法。例如:完全适合所提供的情况(哈希码)。String.getBytes(enc) 相对较慢。

static byte[] toBytes(String s){
        byte[] b=new byte[s.length()*2];
        ByteBuffer.wrap(b).asCharBuffer().put(s);
        return b;
    }
于 2011-01-11T19:29:46.337 回答
-3

区分 UTF-8 和 UTF-16 的简单方法是确定它们之间的共性。

除了为给定字符共享相同的 unicode 编号外,每个字符都有自己的格式。

UTF-8 尝试用一个字节(如果是 ASCII)来表示给字符的每个 unicode 数字,否则 2 个两个字节,否则 4 个字节等等......

UTF-16 尝试表示,每个 unicode 数字都以两个字节开头。如果两个字节不够用,则使用 4 个字节。如果这还不够,则使用 6 个字节。

理论上,UTF-16 更节省空间,但实际上 UTF-8 更节省空间,因为大多数用于处理的字符(98% 的数据)是 ASCII 和 UTF-8 尝试用单字节和 UTF-16 表示它们尝试用 2 个字节来表示它们。

此外,UTF-8 是 ASCII 编码的超集。因此,每个需要 ASCII 数据的应用程序也将被 UTF-8 处理器接受。这不适用于 UTF-16。UTF-16 无法理解 ASCII,这是采用 UTF-16 的一大障碍。

另一点需要注意的是,到目前为止,所有 UNICODE 最多可以容纳 4 个字节的 UTF-8(考虑到世界上所有的语言)。这与 UTF-16 相同,与 UTF-8 相比并没有真正节省空间(https://stackoverflow.com/a/8505038/3343801

因此,人们尽可能使用 UTF-8。

于 2016-03-27T04:59:46.390 回答