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

Java 将字符串转换为字节[]返回错误的值(编码?),java,Java,我从文件中读取字节[],并将其转换为字符串: byte[] bytesFromFile = Files.readAllBytes(...); String stringFromFile = new String(bytesFromFile, "UTF-8"); 我想将其与从web服务获得的另一个字节[]进行比较: String stringFromWebService = webService.getMyByteString(); byte[] bytesFromWebService = s

我从文件中读取
字节[]
,并将其转换为
字符串

byte[] bytesFromFile = Files.readAllBytes(...);
String stringFromFile = new String(bytesFromFile, "UTF-8");
我想将其与从web服务获得的另一个
字节[]
进行比较:

String stringFromWebService = webService.getMyByteString(); 
byte[] bytesFromWebService = stringFromWebService.getBytes("UTF-8");
因此,我从文件中读取
字节[]
并将其转换为
字符串
,然后从web服务中获取
字符串
,并将其转换为
字节[]
。然后我进行以下测试:

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

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

为什么第二个断言失败了?

我不完全理解,但下面是我得到的结果:

问题是,数据包含一些无效的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
时,一切正常。奇怪的是(a还不明白)为什么我的转换(
newstring(bytesFromFile,“UTF-8”);
)没有抛出任何异常(比如我的
isValidUTF8
方法),尽管数据不是有效的UTF-8


但是,我想我会换一种方式,将我的
字节[]
编码为Base64字符串,因为我不想在编码方面遇到更多麻烦。

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

顺便说一句,web服务调用在引擎盖下执行了decond操作;http调用使用头taht定义字符的编码方式,即如何从套接字读取字节,然后转换为字符。因此,调用soapweb服务将返回一个xml(可以编组为Java对象),所有编码操作都已正确完成


因此,如果必须从文件中读取字符,则必须面对编码问题;您可以如您所述使用BASE64,但您失去了文本文件的一个主要优点:文本文件可读性强,易于调试和开发。

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

然而,我认为最有趣的方面不是
byte[]
断言失败,而是
assert
字符串值相同的通过了。我不能百分之百确定这是为什么,但我认为下面对源代码的搜索可能会给我们答案:

  • 看看如何工作-我们看到构造函数调用
  • 如果提供了tht
    UTF-8
    字符集,则依次调用
  • 这将调用which来决定在角色不可映射时应该做什么(我猜如果出现非
    UTF-8
    角色,情况就是这样)
  • 在本例中,它使用一个动作

  • 这意味着它仍然存在,即使它在技术上无法实现

  • 我认为这意味着,即使代码获得了一个不可复制的字符,它也会替换它的最佳猜测-因此我猜测它的最佳猜测是正确的,因此在比较中,
    字符串
    表示是相同的,但
    字节[]
    不再相同

StringCoding.decode()
CharacterCodingException
catch
块表示:

} catch (CharacterCodingException x) {
            // Substitution is always enabled,
            // so this shouldn't happen

如果您尝试转储每个数组,它们在什么点不同?它们在任何值和长度上都不同。他们完全不同。我假设它与编码有关,但我不明白…是否可能发布不同数据的样本(或具有相同问题的某些测试数据)?除非您的文件具有有效的UTF-8编码,否则失败的断言没有什么奇怪的。即使在完全有效的编码情况下,仍然可能存在差异(如字节顺序标记),这些差异可能存在于
字节
数组中,也可能不存在。字符串(byte[],String)是否可以用unicode替换字符替换无效字节?
} catch (CharacterCodingException x) {
            // Substitution is always enabled,
            // so this shouldn't happen