为什么新的String(bytes,enc).getBytes(enc)不返回原始字节数组?

我做了以下“模拟”:

byte[] b = new byte[256]; for (int i = 0; i < 256; i ++) { b[i] = (byte) (i - 128); } byte[] transformed = new String(b, "cp1251").getBytes("cp1251"); for (int i = 0; i < b.length; i ++) { if (b[i] != transformed[i]) { System.out.println("Wrong : " + i); } } 

对于cp1251输出一个错误的字节 – 位置25。
对于KOI8-R – 一切都很好。
对于cp1252 – 4或5的差异。

这是什么原因以及如何克服这个原因?

我知道在任何编码中将字节数组表示为字符串是错误的 ,但它是支付提供商协议的要求,所以我没有选择权。

更新:代表它在ISO-8859-1工作,我将它用于byte[]部分, cp1251用于文本部分,所以问题仍然只是出于好奇

目标集中不支持某些“字节” – 它们被替换为? 字符。 当你转回来, ? 通常转换为字节值63 – 这不是以前的字节值。

这是什么原因

原因是字符编码不一定是双射的,并且没有充分的理由期望它们。 并非所有字节或字节序列在所有编码中都是合法的,并且通常非法序列被解码为某种占位符字符,如“?” 或U + FFFD ,当然在重新编码时不会产生相同的字节。

另外,一些编码可以将一些合法的不同字节序列映射到相同的字符串。

看来cp1251和cp1252的字节值都与定义的字符不对应; 即它们是“不可映射的”。

String(byte[], String)的javadoc说:

当给定字节在给定字符集中无效时,此构造函数的行为未指定。 当需要更多地控制解码过程时,应该使用CharsetDecoder类。

其他建设者这样说:

此方法始终使用此charset的默认替换字符串替换格式错误的输入和不可映射字符序列。

如果你在实践中看到这种事情发生,它表明你使用了错误的字符集,或者你被给了一些不好的数据。 无论哪种方式,继续进行可能不是一个好主意,好像没有问题。

我一直试图弄清楚是否有办法让CharsetDecoder“保留”不可映射的字符,除非你愿意实现自定义解码器/编码器对,否则我认为不可能。 但我也得出结论,即使尝试也没有意义。 它(理论上)错误地将那些不可映射的字符映射到真正的Unicode代码点。 如果你这样做,你的应用程序将如何处理它们?

实际上应该有一个区别:值24的字节被转换为值为0xFFFDchar ; 这是“Unicode替换字符”,用于不可翻译的字节。 转换回来后,您会得到一个问号(值63)。

在CP1251中,代码24表示“输入结束”,不能成为正确字符串的一部分,这就是为什么Java认为它是“不可翻译的”。

历史原因:在古代字符编码(EBCDIC,ASCII)中,前32个代码具有特殊的“控制”含义,它们可能不会映射到可读字符。 示例:退格,响铃,回​​车。 较新的字符编码标准通常会inheritance这一点,并且它们不会为前32个位置中的每一个定义Unicode字符。 Java字符是Unicode。