2010-03-30 38 views
17

我做了下面的“模拟”:为什么新的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的文字部分,因此,问题仍然只是出于好奇

回答

11

某些“字节”在目标集中不受支持 - 它们被替换为?字符。当您转换回来时,通常将?转换为字节值63 - 这不是以前的情况。

+0

太棒了。我实际上是在.NET中寻找答案,但他们在行为上的相似程度足以让我从中收集到。谢谢。 – 2010-03-30 12:33:44

4

看来,这两个CP1251和cp1252的字节值不符合定义的字符;即它们是“不可映射的”。

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

此构造时给出的字节是不是在给定的charset有效的行为是不确定的。当需要对解码过程进行更多的控制时,应该使用CharsetDecoder类。

其他构造这样说:

这个方法总是会替换错误输入和不可映射的字符序列与此charset的默认替换字符串。

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

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

7

,这是什么

究其原因,原因是字符编码不是necesarily bijective并没有充分的理由期待他们。并非所有字节或字节序列在所有编码中都是合法的,并且通常将非法序列解码为某种占位符字符,如'?'或U+FFFD,这当然在重新编码时不会产生相同的字节。

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

3

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

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

2

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

相关问题