当字符串被编码为UTF-8字节时,是否会添加空字符?

3
代码片段:
public static void main(String[] args) {
    String s = "qwertyuiop";
    System.out.println(Arrays.toString(Charset
       .forName("UTF-8")
       .encode(s)
       .array()));
}

输出:

[113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 0]

这似乎是因为,在 java.nio.charset.CharsetEncoder 类中,UTF-8 的 averageBytesPerChar 变量在底层似乎为 1.1。因此它分配了 11 个字节而不是 10 个字节,如果输入字符串只包含单字节字符,则最后会得到奇怪的空字符。
我想知道这是否有任何文档记录?
这个页面:

https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html#encode(java.lang.String)

不提供有关这种行为的线索。
附言:我理解得对,无论如何,上面的片段最好被替换为:
s.getBytes(StandardCharsets.UTF_8)

我从它的来源中看到,它也修剪结果以避免那些空字符?那么,java.nio.charset.Charset的encode(String s)方法是用来做什么的?
2个回答

12

问题不在于Charset.encode(),而是Buffer.array()

如果你打印Charset.forName("UTF-8").encode(s),你会发现输出结果为

java.nio.HeapByteBuffer[pos=0 lim=10 cap=11]

ByteBuffer的限制是10,这是字符串的长度,而容量是11,表示缓冲区总分配大小。如果更改编码,则限制和容量可能会有更大的变化,例如:
System.out.println(Charset.forName("UTF-16").encode(s));
// java.nio.HeapByteBuffer[pos=0 lim=22 cap=41]
// (2 extra bytes because of the BOM, not null-termination)

当你调用.array()时,它将返回整个支持数组,因此即使超出限制的内容也将被包括在内。
提取Java字节数组的实际方法是通过.get()方法
ByteBuffer buf = Charset.forName("UTF-8").encode(s);
byte[] encoded = new byte[buf.limit()];
buf.get(encoded);
System.out.println(Arrays.toString(encoded));

这看起来很混乱,因为"nio"代表本地I/O。Buffer类型被创建是为了可以轻松地包装C数组。它使得与本地代码的交互,例如读/写文件或发送/接收网络数据变得非常高效。这些NIO API通常直接使用Buffer,而不需要在中间构造任何byte[]。如果您只使用Buffer,则中间两行不需要存在 :)。
如果整个操作仅限于Java内部,则只需调用s.getBytes(StandardCharsets.UTF_8)即可。

0

如果您只想查看字符串作为UTF8编码的字节数组,则可以简单地使用getBytes(Charset charset)方法。它可能看起来像这样:

String hello = "qwertyuiop";   
byte[] helloBytes_UTF_8 = hello.getBytes(StandardCharsets.UTF_8);

您会发现每个字符有2个字节。您的输出符合StandardCharsets.ISO_8859_1字符集。如果您想尝试不同的编码,我建议使用一个小型开源库,其中包含一些实用程序之一允许您将字符串转换为UNICODE(UTF-8)表示形式并返回。Open Source Java library with stack trace filtering, Silent String parsing Unicode converter and Version comparison。本文介绍了该库以及如何使用它。您也可以下载源代码和javadoc。特别是要查找“String Unicode converter”段落。使用此类,您将把您的字符串"qwertyuiop"转换为:"\u0071\u0077\u0065\u0072\u0074\u0079\u0075\u0069\u006f\u0070" \ u符号后的每四个数字表示十六进制表示中的2个字节(一个字符)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接