Java bug?为什么utf8编码中有额外的零字节?

时间:2012-07-03 21:31:20

标签: java utf-8 character-encoding

以下代码

public class CharsetProblem {
public static void main(String[] args) {
    //String str = "aaaaaaaaa";
    String str = "aaaaaaaaaa";
    Charset cs1 = Charset.forName("ASCII");
    Charset cs2 = Charset.forName("utf8");

    System.out.println(toHex(cs1.encode(str).array()));
    System.out.println(toHex(cs2.encode(str).array()));

}

public static String toHex(byte[] outputBytes) {

    StringBuilder builder = new StringBuilder();

    for(int i=0; i<outputBytes.length; ++i) {
        builder.append(String.format("%02x", outputBytes[i]));
    }

    return builder.toString();
}
}

返回

61616161616161616161
6161616161616161616100

即。 utf8编码返回多余的字节。如果我们减少a-s,那么我们就没有多余的字节。如果我们采用更多的a-s,我们可以得到越来越多的字节。

为什么?

如何解决这个问题?

3 个答案:

答案 0 :(得分:7)

你不能只获得支持数组并使用它。 ByteBuffers有一个capacity, position and a limit

System.out.println(cs1.encode(str).remaining());
System.out.println(cs2.encode(str).remaining());

产生

10
10

请改为尝试:

public static void main(String[] args) {
  //String str = "aaaaaaaaa";
  String str = "aaaaaaaaaa";
  Charset cs1 = Charset.forName("ASCII");
  Charset cs2 = Charset.forName("utf8");

  System.out.println(toHex(cs1.encode(str)));
  System.out.println(toHex(cs2.encode(str)));
}

public static String toHex(ByteBuffer buff) {
  StringBuilder builder = new StringBuilder();
  while (buff.remaining() > 0) {
    builder.append(String.format("%02x", buff.get()));
  }
  return builder.toString();
}

它产生了预期的:

61616161616161616161
61616161616161616161

答案 1 :(得分:6)

您假设ByteBuffer的支持数组恰好是保存内容的正确大小,但不一定如此。实际上,内容甚至不需要从数组的第一个字节开始!研究ByteBuffer的API,您将了解正在发生的事情:内容从arrayOffset()返回的值开始,limit()返回结束。

答案 2 :(得分:2)

已经给出了答案,但是当我遇到同样的问题时,我认为提供更多细节可能会有用:

通过调用cs1.encode(str).array()cs2.encode(str).array()返回的字节数组返回对当时分配给ByteBuffer的整个数组的引用。阵列的容量可能大于实际使用的容量。要仅检索已使用的部分,您应该执行以下操作:

ByteBuffer bf1 = cs1.encode(str);
ByteBuffer bf2 = cs2.encode(str);
System.out.println(toHex(Arrays.copyOf(bf1.array(), bf1.limit())));
System.out.println(toHex(Arrays.copyOf(bf2.array(), bf2.limit())));

这会产生您期望的结果。