有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java、python和javascript应用程序之间的Base64 UTF16编码

作为一个示例,我有以下字符串,我假定它是utf-16编码的:“hühüh”

在python中,我在编码时得到以下结果

>>> base64.b64encode("hühühüh".encode("utf-16"))
b'//5oAPwAaAD8AGgA/ABoAA=='

在java中:

>>> String test = "hühühüh";
>>> byte[] encodedBytes = Base64.getEncoder().encode(test.getBytes(StandardCharsets.UTF_16));
>>> String testBase64Encoded = new String(encodedBytes, StandardCharsets.US_ASCII);
>>> System.out.println(testBase64Encoded);
/v8AaAD8AGgA/ABoAPwAaA==

在javascript中,我根据Mozilla dev guideline定义了一个二进制编码函数,然后对相同的字符串进行编码

>> function toBinary(string) {                                                                                                                            
      const codeUnits = new Uint16Array(string.length);
      for (let i = 0; i < codeUnits.length; i++) {
          codeUnits[i] = string.charCodeAt(i);
      }
      return String.fromCharCode(...new Uint8Array(codeUnits.buffer));
  }
>> atob(toBinary("hühühüh"))

aAD8AGgA/ABoAPwAaAA=

如您所见,每个编码器都创建了一个不同的base64字符串。让我们再次反向编码

在Python中,所有生成的字符串都会再次进行良好解码:

>>> base64.b64decode("//5oAPwAaAD8AGgA/ABoAA==").decode("utf-16")
'hühühüh'
>>> base64.b64decode("/v8AaAD8AGgA/ABoAPwAaA==").decode("utf-16")
'hühühüh'
>>> base64.b64decode("aAD8AGgA/ABoAPwAaAA=").decode("utf-16")
'hühühüh'

在javascript中,根据Mozilla dev guideline再次使用fromBinary函数:

>>> function fromBinary(binary) {
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < bytes.length; i++) {
    bytes[i] = binary.charCodeAt(i);
 }
  console.log(...bytes)
  return String.fromCharCode(...new Uint16Array(bytes.buffer));
}
>>> fromBinary(window.atob("//5oAPwAaAD8AGgA/ABoAA=="))
"\ufeffhühühüh"
>>> fromBinary(window.atob("/v8AaAD8AGgA/ABoAPwAaA=="))
"\ufffe栀ﰀ栀ﰀ栀ﰀ栀"
>>> fromBinary(window.atob("aAD8AGgA/ABoAPwAaAA="))
"hühühüh"

最后在Java中:

>>> String base64Encoded = "//5oAPwAaAD8AGgA/ABoAA==";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println(base64Decoded);
hühühüh
>>> String base64Encoded = "/v8AaAD8AGgA/ABoAPwAaA==";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println(base64Decoded);
hühühüh
>>> String base64Encoded = "aAD8AGgA/ABoAPwAaAA=";
>>> byte[] asBytes = Base64.getDecoder().decode(base64Encoded);
>>> String base64Decoded = new String(asBytes, StandardCharsets.UTF_16);
>>> System.out.println("Decoded" + base64Decoded);
hühühüh

我们可以看到,python的base64解码器能够为其他两个解析器编码和解码消息。但是Java和Javascript解析器之间的定义似乎并不兼容。我不明白这是为什么。 这是Java和Javascript中的base64库的问题吗?如果是的话,是否有其他工具或路径允许我们在Java和Javascript应用程序之间传递base64编码的utf-16字符串?如何通过使用尽可能接近核心语言功能的工具,确保Java和Javscript应用程序之间安全的base64字符串传输

编辑: 正如公认的答案中所说,问题在于utf16编码不同。Java和Javascript之间的兼容性问题可以通过在Javascript中以相反顺序生成utf16字节来解决,也可以接受编码字符串为StandardCharsets.UTF_16LE


共 (1) 个答案

  1. # 1 楼答案

    问题是UTF-16有4种变体

    这种字符编码每个代码单元使用两个字节。两个字节中哪一个应该先出现?这就产生了两种变体:

    • UTF-16BE首先存储最高有效字节
    • UTF-16LE首先存储最低有效字节

    为了区分这两者之间的差异,文本开头有一个可选的“字节顺序标记”(byte order mark,BOM)字符U+FEFF。所以带有BOM的UTF-16BE以字节fe ff开头,而带有BOM的UTF-16LE以ff fe开头。因为BOM是可选的,所以它的存在使可能的编码数量增加了一倍

    看起来您正在使用4种可能的编码中的3种:

    • Python将UTF-16LE与BOM一起使用
    • Java使用UTF-16BE和BOM
    • JavaScript使用UTF-16LE,没有BOM

    人们更喜欢UTF-8而不是UTF-16的原因之一是为了避免这种混淆