2011-02-15 248 views
4

我有以下的Java代码:将字节数组转换为字符串并返回字节数组时,为什么长度不同?

byte[] signatureBytes = getSignature(); 

String signatureString = new String(signatureBytes, "UTF8"); 
byte[] signatureStringBytes = signatureString.getBytes("UTF8"); 

System.out.println(signatureBytes.length == signatureStringBytes.length); // prints false 

问:我可能误解这一点,但我认为new String(byte[] bytes, String charset)String.getBytes(charset)互为逆运算?

问:作为后续,什么是一个安全的方式来传输一个字节[]数组作为一个字符串?

回答

8

并非每个byte[]都是有效的UTF-8。默认情况下,无效序列被固定字符替换,我认为这是长度变化的原因。

尝试拉丁-1,它不应该发生,因为它是一个简单的编码,其中每个byte[]是有意义的。

对于Windows-1252都不应该发生。这里有未定义的序列(实际上是未定义的字节),但是所有字符都被编码在单个字节中。新的byte[]可能与原来的不同,但它们的长度必须相同。

+0

工作。什么是一个安全的方式来传输一个byte []数组作为字符串呢? – John 2011-02-15 23:02:13

+3

org.apache.commons.codec.binary.Base64永远是您传递任意数据的最好朋友:) – Affe 2011-02-15 23:03:31

2

想到两种可能性。

首先是你的签名不是完全有效的UTF8。你不能只是采取任何二进制数据和“串”它。并非每一个比特都定义了一个合法的字符。 String构造函数将为二进制数据插入一些默认的替换内容,这些内容实际上并不意味着UTF8中的任何内容。这不是一个可逆过程。如果你想“串”一些任意的二进制数据,你需要使用已建立的方法来做到这一点,我会建议org.apache.commons.codec.binary.Base64

还有一些字符有超过一个表示。例如,有口音的东西可以被编码为重音字符,或者字符加上之后的重音将被组合。在编码之间来回移动时,不能保证这是一个可逆的过程。

5

我可能误解了这一点,但我认为新的字符串(字节[]字节,字符串字符集)和String.getBytes(字符集)是逆操作?

不一定。

如果输入字节数组包含的序列不是有效的UTF-8,那么初始转换可能会将它们变成(例如)问号。然后第二个操作将它们转换为与原始表示不同的UTF-8编码的'?'个字符....


确实,Unicode中的某些字符有多个表示;例如重音字符可以是单个码点,也可以是基本字符码点和重音码点。但是,在字节数组(包含有效的UTF-8)和字符串之间来回转换应保留码点序列。它不执行任何“标准化”。


那么,什么将是运送byte []数组作为字符串,然后以安全的方式?

最安全的选择是base64编码字节数组。这具有附加的优点,即字符串中的字符将不会转换为任何可以表示拉丁字母和数字的字符集/编码。

另一种选择是使用Latin-1而不是UTF-8。但是:

  • 如果数据得到(例如)被错误地解释为UTF-8,则存在损害的风险。
  • 如果将“字符串”嵌入到XML中,则此方法不合法。许多控制字符不在XML字符集内,并且不能在XML文档中使用,即使编码为字符实体。