UTF-8和UTF-16之间的区别?

问题

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();

#1 热门回答(265 赞)

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

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(基本多语言平面)字符,包括拉丁语,西里尔语,大多数中文(中华人民共和国支持BMP强制性以外的一些代码点),大多数日语可以用2个字节表示。如果文本不包含增补字符,这可以加快索引和计算代码点计数。
  • 即使文本具有补充字符,它们仍然由成对的16位值表示,这意味着总长度仍然可以被2整除,并允许使用16位字符作为字符串的基本组件。

主要UTF-16缺点:

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

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


#2 热门回答(18 赞)

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

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

对于BMP中的字符,UTF-8使用1到3个字节,对于U 0000到U 1FFFFF的当前Unicode范围内的字符最多使用4个字符,如果有必要,则可扩展到U 7FFFFFFF ...但特别是所有ASCII字符分别以单个字节表示。

出于消息摘要的目的,只要尝试重新创建摘要的每个人都使用相同的选项,你选择哪一个都无关紧要。

有关UTF-8和Unicode的更多信息,请参见this page

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


#3 热门回答(3 赞)

这与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;
    }