为何UTF-8下byte[]→String→byte[]输入输出不一致?ISO_8859_1是否始终一致?
问题解答
为什么UTF-8下byte[]→String→byte[]会出现输入输出差异?
没错,这确实和UTF-8的可变宽度编码特性密切相关,但更核心的原因是UTF-8对字节序列的合法性有严格要求。
咱们先拆解UTF-8的编码逻辑:它用1-4个字节表示一个Unicode字符,而且不是随便组合的字节都算合法序列。比如:
- 单字节字符的最高位必须是0(范围0x00-0x7F)
- 多字节字符的首字节以
110、1110、11110开头,后续字节必须以10开头
如果你的inputBytes里包含不符合规则的字节(比如单独的0xFF,或者半截的多字节序列),把它转成String时,Java会自动把这些非法UTF-8字节替换成Unicode的替换字符�(U+FFFD)。等你再把这个String转回byte[]时,替换字符会被编码成UTF-8格式的三个字节:0xEF 0xBF 0xBD,这自然和原输入字节不一样,测试用例也就失败了。
举个直观的代码例子:
import java.util.Arrays; import java.nio.charset.StandardCharsets; public class CharsetDemo { public static void main(String[] args) { byte[] inputBytes = new byte[]{(byte) 0xFF}; // 非法UTF-8单字节 String str = new String(inputBytes, StandardCharsets.UTF_8); byte[] outputBytes = str.getBytes(StandardCharsets.UTF_8); // 对比结果:inputBytes是[0xFF],outputBytes是[0xEF, 0xBF, 0xBD] System.out.println("输入字节:" + Arrays.toString(inputBytes)); System.out.println("输出字节:" + Arrays.toString(outputBytes)); } }
为什么ISO_8859_1下转换不会有问题?
ISO_8859_1(又称Latin-1)是单字节固定宽度编码,它的设计逻辑就是把每个字节(0x00-0xFF)直接映射到Unicode的U+0000到U+00FF字符。也就是说:
- 任何字节值在ISO_8859_1里都是合法编码
- 转成
String时,每个字节对应唯一的Unicode字符,没有替换逻辑 - 转回
byte[]时,每个Unicode字符又会还原成原来的字节值
不管你的inputBytes数组多大、包含什么字节,这个1:1的映射关系都不会断裂,所以测试总能通过。
附加问题:ISO_8859_1的byte[]→String→byte[]转换是否始终一致?
是的,绝对一致。
因为ISO_8859_1的编码空间刚好覆盖了所有可能的字节值(0x00到0xFF),每个字节和Unicode字符是严格一一对应的,不存在任何需要替换的非法序列。只要你全程使用ISO_8859_1字符集进行转换,输入和输出的byte[]就会完全相同,没有任何损耗或变化。
内容的提问来源于stack exchange,提问作者PresentProgrammer
相关产品推荐
相关产品推荐

