Java 字符串 "hello" 在 getBytes("UTF-16") 时有 12 个字节?

Java string "hello" has 12 bytes when getBytes("UTF-16")?

我预计,当一个 java 字符存储为 "UTF-16" 时,每个字符使用 2 个字节,所以 "hello" 应该消耗 10 个字节,但是这段代码:

String h = "hello";
System.out.println(new String(h.getBytes("UTF-16"), "UTF-16").length());
System.out.println(new String(h.getBytes("UTF-8"), "UTF-8").getBytes("UTF-16").length);

将打印“5 12”

我的问题:

(1) 正如我提到的,我预计第一个 println 应该得到“10”。但为什么是 5?

(2) 对于第二个 println,我试图首先将其作为 "UTF-8" 然后作为 "UTF-16" 获取字节。我想它也应该是10。但实际上是12。

我正在使用 MAC,我所在的地区是香港。你能帮忙解释一下程序中发生了什么,以及“5 12”实际上是如何产生的吗?

非常感谢!

(1) I expected that the first println should get "10" as I mentioned. But why 5?

您采用 5 个字符的字符串,使用 UTF-16 编码将其编码为字节。
然后你通过从 UTF-16 中(正确地)解码字节来创建一个新的字符串,它再次为你提供一个由原来的 5 个字符组成的新字符串。

(2) For the second println, I am trying to getBytes for it first as "UTF-8" then as "UTF-16". I suppose it should also be 10. But actually it's 12.

这部分代码:

    new String(h.getBytes("UTF-8"), "UTF-8")

实际上是空操作。这只是复制字符串的一种相当昂贵的方法。您使用 UTF-8 作为编码方案将字符串编码为字节,然后通过解码 UTF-8 编码字节来创建新字符串。

如此有效,您正在这样做:

    "hello".getBytes("UTF-16").length

多出2个字节的原因是UTF-16编码将BOM(字节顺序标记)作为第一个(2字节)代码单元。

有关详细信息,请阅读 "UTF-8, UTF-16, UTF-32 & BOM" 上的 Unicode 常见问题解答。

I expected that the first println should get "10" as I mentioned. But why 5?

您在 String 上呼叫 length(),而不是 byte[]。因此,这将为您提供 个字符 中的字符串长度(至少只要我们停留在 Unicode 基本多语言平面中——不幸的是,当您有需要可变的字符时,这会崩溃- 即使在 UTF-16 中也是长度编码)。

一旦你有了一个字符串,使用什么编码来创建它就无关紧要了。 length 总是以字符形式给出。

如果您使用 UTF-16 将其转换为 byte[],您可能理所当然地期望 10(五个字符乘以每个两个字节)——它实际上最终为 12 是由于包含字节顺序标记。