如何将UTF8 ArrayBuffer转换为UTF16 JavaScript字符串

8
这里的答案帮助我开始学习如何使用ArrayBuffer: 在字符串和ArrayBuffer之间进行转换 然而,它们有很多不同的方法。主要的方法是这个:
function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}

function str2ab(str) {
  var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
  var bufView = new Uint16Array(buf);
  for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

我想澄清一下UTF8和UTF16编码之间的区别,因为我不确定这个说法是否正确。

在JavaScript中,据我了解,所有的字符串都是UTF16编码的。但你自己的ArrayBuffer中可能包含任何编码的原始字节

假设我已经提供了一个ArrayBuffer给浏览器,而这些来自后端的字节是以UTF8编码的:

var r = new XMLHttpRequest()
r.open('GET', '/x', true)
r.responseType = 'arraybuffer'
r.onload = function(){
  var b = r.response
  if (!b) return
  var v = new Uint8Array(b)
}
r.send(null)

现在我们从响应r中获得了ArrayBuffer b,并将其放入了Uint8Array视图v中。
问题是,如果我想将它转换为JavaScript字符串,该怎么做。
据我所知,v中的原始字节以UTF8编码(并且已经以UTF8编码发送到浏览器)。但如果我们这样做,我认为它不会正常工作:
function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}

根据我的理解,我们使用的是UTF8编码,而JavaScript字符串使用的是UTF16编码,因此您需要这样做:

function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint8Array(buf));
}

第一个问题是使用Uint8Array而不是Uint16Array。那么,如何将utf8字节转换为js字符串。

第二个问题是如何从JavaScript字符串返回UTF8字节。也就是说,我不确定这样编码是否正确:

function str2ab(str) {
  var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
  var bufView = new Uint16Array(buf);
  for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

我不确定在这种情况下应该做出什么改变,以便返回一个UTF8的ArrayBuffer。类似这样的东西似乎是不正确的:

function str2ab(str) {
  var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
  var bufView = new Uint8Array(buf);
  for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

无论如何,我只是试图澄清如何从后端编码字符串的UTF8字节转换为前端的UTF16 JavaScript字符串。

"String.fromCharCode.apply(null, new Uint8Array(buf))" - 不行,那只适用于ASCII字符串。你需要一个正确的TextDecoder(以及一个TextEncoder进行反转)。 - Bergi
2个回答

2

我们需要一些假设来理解发生了什么:

1. JS使用UTF-16

首先,JS使用UTF-16存储符号,正如在Unicode字符串部分中提到的那样: https://developer.mozilla.org/en-US/docs/Web/API/btoa

2. UTF-16和UTF-8

UTF-8和UTF-16并不意味着一个符号由一个字节或两个字节表示。UTF-8和UTF-16都是可变长度编码。

3. ArrayBuffer和编码

"hello" by one byte (Uint8Array): [104, 101, 108, 108, 111]
the same by two bytes (Uint16Array): [0, 104, 0, 101, 0, 108, 0, 108, 0, 111]

ArrayBuffer 中没有编码,因为 ArrayBuffer 表示数字。

对第二个数组的迭代将与对第一个数组的迭代不同。你知道两个字节的数字不能打包成一个字节的数字。


当您从服务器以utf-8格式接收响应时,您将其作为字节序列接收,如果接收到的数据按每个符号一个字节存储,则您的代码将正常工作 - 它可以处理像[a-zA-Z0-9]和常见标点符号这样的符号。但是,如果您接收到一个在UTF-8中使用两个字节存储的符号,则转录到UTF-16将不正确:

0xC3 0xA5 (one symbol å) -> 0x00 0xC3 0x00 0xA5 (two symbols "Ã¥")

所以,如果您不会将符号传输到拉丁符号、数字和标点符号范围之外,您可以使用您的代码,即使它不正确也能正常工作。


2
为什么不使用TextDecoder接口,而要自己编写代码呢?你是否受限于不支持该接口的浏览器?
const decoder = new TextDecoder('UTF-8')
const dataStr = decoder.decode(dataBuf) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接