Java使用Charset转换字节数组时输出字符串末尾出现菱形乱码
问题根因
你遇到的乱码问题和Android Studio Logcat、文件编码都没有关系,是对CharBuffer API的使用错误导致的。Charset.decode() 返回的CharBuffer本质上是一个视图对象,array()方法返回的是其底层完整的后备数组,而非仅包含有效字符的部分。UTF-8是变长编码,解码器无法预先精准计算输出char的长度,会预分配大于实际需求的数组空间,未使用的位置默认填充\u0000空字符,你直接将整个数组转为字符串输出时,这些空字符就会被显示为菱形乱码。而ISO-8859-1是单字节定长编码,每个输入字节刚好对应一个输出char,预分配数组长度和实际有效长度完全一致,所以不会出现乱码。
3字符场景下没有问题的原因是短长度输入刚好和解码器预分配的数组长度匹配,没有多余的填充空间。
解决方法
- 如果你需要获取仅包含有效字符的char数组,且不想通过String类转换(符合敏感数据安全要求),可以按有效长度截取内容:
import java.nio.charset.StandardCharsets; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Arrays; Charset charset = StandardCharsets.UTF_8; ByteBuffer inputBuffer = ByteBuffer.wrap(testbytes); CharBuffer charBuffer = charset.decode(inputBuffer); // 计算有效字符长度 int validLength = charBuffer.limit() - charBuffer.position(); char[] result = new char[validLength]; // 将有效字符拷贝到新数组,不会携带填充的空字符 charBuffer.get(result); // 敏感数据使用完后可手动清空数组避免内存残留:Arrays.fill(result, (char)0);
- 如果需要复用解码器避免重复实例化的开销,可手动创建
CharsetDecoder,每次解码前调用reset()清除状态,避免多次调用的内容残留:
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); // 第一次解码 decoder.reset(); CharBuffer buffer1 = decoder.decode(ByteBuffer.wrap(test1b)); // 第二次解码 decoder.reset(); CharBuffer buffer2 = decoder.decode(ByteBuffer.wrap(test2b));
注意:永远不要直接使用CharBuffer.array()的返回值作为最终结果,该数组是缓冲区的内部实现,仅用于底层操作,必须结合position()、limit()、arrayOffset()三个参数截取有效区间使用。
内容的提问来源于stack exchange,提问作者Ken
相关产品推荐
相关产品推荐

