为啥 new String(bytes, enc).getBytes(enc) 不返回原始字节数组?

Posted

技术标签:

【中文标题】为啥 new String(bytes, enc).getBytes(enc) 不返回原始字节数组?【英文标题】:Why new String(bytes, enc).getBytes(enc) does not return the original byte array?为什么 new String(bytes, enc).getBytes(enc) 不返回原始字节数组? 【发布时间】:2010-03-30 12:12:53 【问题描述】:

我做了以下“模拟”:

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 用于文本部分,所以这个问题只是出于好奇

【问题讨论】:

【参考方案1】:

目标集中不支持某些“字节” - 它们被替换为? 字符。当您转换回来时,? 通常会转换为字节值 63 - 这与以前不同。

【讨论】:

太棒了。我实际上是在 .NET 中寻找答案,但它们在行为上都足够相似,因此我从中收集到了它。谢谢。【参考方案2】:

这是什么原因

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

此外,某些编码可能会将某些合法的不同字节序列映射到同一个字符串。

【讨论】:

【参考方案3】:

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

String(byte[], String) 的 javadoc 是这样说的:

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

其他构造函数是这样说的:

此方法总是用此字符集的默认替换字符串替换格式错误的输入和不可映射的字符序列。

如果您在实践中看到这种情况发生,则表明您使用了错误的字符集,或者您收到了一些错误的数据。无论哪种方式,如果没有问题就继续下去可能不是一个好主意。

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

【讨论】:

【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

【讨论】:

以上是关于为啥 new String(bytes, enc).getBytes(enc) 不返回原始字节数组?的主要内容,如果未能解决你的问题,请参考以下文章

java中文乱码,能说下string.getBytes()和new String()转码是,具体点。

为啥使用 Convert.FromBase64String(...) 将字符串转换为 byte[] 时,我不能放两个相同的字符? [复制]

golang string和[]byte的对比

当我将它放入与将其从数据库中拉出时,new String(byte[]) 会给出不同的结果

Golang string和[]byte的对比

java中byte数组怎么转换成string类型