String.getBytes(“UTF-16”)会在所有平台上返回相同的结果吗?

时间:2014-09-16 19:10:50

标签: java string encoding

我需要从包含用户密码的String创建哈希。要创建哈希,我使用一个字节数组,通过调用String.getBytes()得到。但是当我在一个不是默认编码的平台上使用指定的编码(例如UTF-8)调用此方法时,非ASCII字符将被默认字符替换(如果我正确理解了getBytes()的行为)因此在这样的平台上,我将得到一个不同的字节数组,最终得到一个不同的哈希值。

由于字符串内部存储在UTF-16中,调用String.getBytes("UTF-16")会保证我在每个平台上都获得相同的字节数组,而不管其默认编码是什么?

3 个答案:

答案 0 :(得分:4)

是。它不仅保证是UTF-16,而且是the byte order is defined too

  

解码时,UTF-16字符集解释输入流开头的字节顺序标记,以指示流的字节顺序,但如果没有字节顺序标记则默认为big-endian;在编码时,它使用big-endian字节顺序并写入一个big-endian字节顺序标记。

(当来电者没有要求时,BOM不相关,因此String.getBytes(...)不会包含它。)

只要你有相同的字符串内容 - 即char值的相同序列 - 那么你就会在每次执行Java时获得相同的字节,除非出现错误。 (鉴于UTF-16可能是在Java中实现的最简单的编码,任何此类错误都会非常令人惊讶......)

事实上,UTF-16是char(通常用于String)的本机表示,但这只与实现的易用性有关。例如,我期望String.getBytes("UTF-8")在每个平台上提供相同的结果。

答案 1 :(得分:1)

确实,java在内部使用Unicode,因此它可以组合任何脚本/语言。 String和char使用UTF-16BE,但.class文件以UTF-8存储String常量。一般来说,它与String的作用无关,因为转换为指定字节必须在的编码的字节。

如果字节的这种编码不能代表某些Unicode字符,则会给出占位符字符或问号。字体也可能没有所有Unicode字符,完整Unicode字体为35 MB是正常大小。然后,您可能会看到一个带有2x2十六进制代码的正方形,以便丢失代码点。或者在Linux上,另一种字体可能会替换char。

因此,UTF-8是一个完美的选择。

String s = ...;
if (!s.startsWith("\uFEFF")) { // Add a Unicode BOM
    s = "\uFEFF" + s;
}
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);

UTF-16(以字节顺序)和UTF-8始终存在于JRE中,而某些字符集则不存在。因此,您可以使用StandardCharsets中的常量,而不需要处理任何UnsupportedEncodingException。

上面我特别为Windows Notepad添加了BOM,以识别UTF-8。它肯定是良好做法。但在这里作为一个小帮助。

UTF16-LE或UTF-16BE没有缺点。我认为UTF-8更普遍使用,因为UTF-16也不能以16位存储所有Unicode代码点。文本是亚洲脚本会更加压缩,但由于HTML标记和其他拉丁文脚本,HTML页面在UTF-8中更紧凑。

对于Windows,UTF-16LE可能更原生。

非Unicode平台(尤其是Windows)的占位符问题可能会发生。

答案 2 :(得分:0)

我刚刚发现了这个:

https://github.com/facebook/conceal/issues/138

似乎回答了你的问题。

根据Jon Skeet的回答:规范很明确。但我想Dalvik / JVM的Android / Mac实现并不同意。