将字符串转换为byte []会返回错误的值(编码?)

时间:2015-03-24 16:33:34

标签: java

我从文件中读取byte[]并将其转换为String

byte[] bytesFromFile = Files.readAllBytes(...);
String stringFromFile = new String(bytesFromFile, "UTF-8");

我想将此与我从网络服务获得的另一个byte[]进行比较:

String stringFromWebService = webService.getMyByteString(); 
byte[] bytesFromWebService = stringFromWebService.getBytes("UTF-8");

因此,我从文件中读取byte[]并将其转换为String,然后从我的网络服务获得String并将其转换为byte[]。然后我做了以下测试:

// works!
org.junit.Assert.assertEquals(stringFromFile, stringFromWebService);

// fails!
org.junit.Assert.assertArrayEquals(bytesFromFile, bytesFromWebService);

为什么第二个断言失败?

3 个答案:

答案 0 :(得分:1)

其他答案涵盖了文件未UTF-8编码的可能事实,从而导致所描述的症状。

但是,我认为最有趣的方面不是byte[]断言失败,而是字符串值相同的assert 传递 即可。我不是百分之百确定这是为什么,但我认为通过源代码进行以下拖网可能会给我们答案:

  • 查看new String(bytesFromFile, "UTF-8");的工作原理 - 我们看到构造函数调用StringCoding.decode()
  • 反过来,如果提供UTF-8字符集,则拨打StringDecoder.decode()
  • 调用CharsetDecoder.decode()来决定如果角色不可映射该怎么办(如果出现非UTF-8字符,我认为会是这种情况)
  • 在这种情况下,它使用操作defined by

    private CodingErrorAction unmappableCharacterAction
        = CodingErrorAction.REPORT;
    
  • 这意味着它仍然是reports the character it has decoded,即使它在技术上是不可映射的。

  • 我认为这意味着即使代码获得了一个无法映射的字符,它也会替代其最佳猜测 - 因此我猜测它的最佳猜测是正​​确的,因此String表示相同比较,但byte[]不再相同。

catchCharacterCodingException的{​​{1}}块表示:

这一假设得到了支持。
StringCoding.decode()

答案 1 :(得分:0)

我完全不明白,但这就是我得到的东西:

问题是数据包含一些字节,这些字节不是有效的UTF-8字节,我通过以下检查知道:

// returns false for my data!
public static boolean isValidUTF8(byte[] input) {
    CharsetDecoder cs = Charset.forName("UTF-8").newDecoder();
    try {
        cs.decode(ByteBuffer.wrap(input));
        return true;
    }
    catch(CharacterCodingException e){
        return false;
    }       
}

当我将编码更改为ISO-8859-1时,一切正常。奇怪的事情(我还不明白)是为什么我的转换(new String(bytesFromFile, "UTF-8");)不会抛出任何异常(比如我的isValidUTF8方法),尽管数据不是有效的UTF-8。

但是,我想我会转到另一个并在Base64字符串中对byte[]进行编码,因为我不希望编码更麻烦。

答案 2 :(得分:0)

您的代码中的真正问题是您不知道真正的文件编码是什么。 当您从Web服务中读取字符串时,您会得到一系列字符;当您将字符串从字符转换为字节时,转换是正确的,因为您指定了如何使用特定编码转换字节数(" UFT-8")。当您阅读文本文件时,您遇到了另一个问题。您有一个需要转换为字符的字节序列。为了正确地执行它,您必须知道如何将字符转换为字节,即文件编码是什么。对于文件(除非指定),它是一个平台常量;在Windows上,文件以win1252编码(非常接近ISO-8859-1);它取决于linux / unix,我认为UTF8是默认的。

顺便说一下,Web服务调用在引擎盖下进行了第二次操作; http调用使用标题taht定义如何编码字符,即如何从套接字读取字节然后转换为字符。因此,调用SOAP Web服务会返回一个xml(可以编组到Java对象中),并且所有编码操作都已正确完成。

因此,如果您必须从文件中读取字符,则必须面对编码问题;你可以按照你的说法使用BASE64,但是你失去了文本文件的一个主要好处:人类可读,易于调试和开发。