如何将ArrayBuffer转换为字符串

4

我已经在node.js上编写了一个简单的TCP服务器,用于向Chrome应用程序发送一些数据。在Chrome应用程序中,当我接收到数据时,我使用下面的函数将其转换为字符串,但是出现了异常 "Uint16Array的字节长度应该是2的倍数"

String.fromCharCode.apply(null, new Uint16Array(buffer))

我找不到任何关于这是什么原因以及如何解决的信息。非常感谢您对此的任何指引。

以下是用于向客户端发送数据的node.js服务器中的代码:

socket.on('data', function(data) {

    console.log('DATA ' + socket.remoteAddress + ': ' + data);
    // Write the data back to the socket, 
    //   the client will receive it as data from the server
    var r= socket.write('from server\r\n');

});

以下是来自Chrome应用程序的代码:
  chrome.sockets.tcp.onReceive.addListener(function (info) {
            console.log('onListener registered');
            if (info.socketId != socketid)
                return;
            else {
                try {

                   data = ab2str(info.data);
                    console.log(data);
                }
                catch (e) {
                    console.log(e);
                }

            }
            // info.data is an arrayBuffer.
        });

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

你能展示一下获取缓冲区的更多细节吗? - Victory
一般来说,这个问题是经典的。不过我不会重复标记它。 - Xan
4个回答

13

1
对于不支持此API的其他浏览器,可以使用TextDecoder shim/polyfill - cuixiping

9
您可能遇到的问题是,您的应用程序在套接字上接收了奇数个字节,但您正试图从中创建一个包含2字节宽度项的数组(因为这适配于 Uint16Array )。如果您的应用程序通过网络接收字符串"Hello"(5个字节),则可以将其转换为 Uint8Array ,并且内容如下:
Item:        0   1   2   3   4
Char:        H   e   l   l   o
Uint8 Value: 72  101 108 108 111

将其转换为Uint16Array,但将尝试执行以下操作:
Item   0     1     2
Chars  He    ll    o?
IntVal 25928 27756 ?????

没有第6个字节可用,因此无法构建数组,因此您会收到异常。
仅在套接字上期望UCS-2字符串数据时,使用Uint16Array才有意义。如果您接收到的是普通ASCII数据,则应将其转换为Uint8Array,并在该数组上映射String.fromCharCode。如果是其他内容,例如UTF-8,则必须进行其他转换。
无论如何,套接字层始终可以自由地以任意长度的块发送数据。您的应用程序必须处理奇怪的大小,并保存任何无法立即处理的剩余部分,以便在接收到下一块数据时使用。

这取决于应用程序 - 你可能想使用Uint8Array,或者像我在上一段中所说的那样,缓冲任何尾随字节,然后在获取下一个数据包时将其用作第一个字节。 - Ian Clelland
我的问题是,我有一个返回错误的arraybuffer responsetype,但错误信息中的一部分文本包含带重音符号的字符,它们被解码为垃圾字符,几乎无法阅读。我尝试像OP一样使用Uint16Array进行解码,但遇到了相同的错误。最终我找到了这个:https://gist.github.com/boushley/5471599,我能够正确地解码字符串,而不需要使用Uint16Array。 - CesarD

2

虽然有点老,但也许使用这个函数(原始来源)会更好(对我来说,在将arraybuffer解码为字符串时,它能够避免一些特殊字符成为完全垃圾的情况):

function decodeUtf8(arrayBuffer) {
  var result = "";
  var i = 0;
  var c = 0;
  var c1 = 0;
  var c2 = 0;

  var data = new Uint8Array(arrayBuffer);

  // If we have a BOM skip it
  if (data.length >= 3 && data[0] === 0xef && data[1] === 0xbb && data[2] === 0xbf) {
    i = 3;
  }

  while (i < data.length) {
    c = data[i];

    if (c < 128) {
      result += String.fromCharCode(c);
      i++;
    } else if (c > 191 && c < 224) {
      if( i+1 >= data.length ) {
        throw "UTF-8 Decode failed. Two byte character was truncated.";
      }
      c2 = data[i+1];
      result += String.fromCharCode( ((c&31)<<6) | (c2&63) );
      i += 2;
    } else {
      if (i+2 >= data.length) {
        throw "UTF-8 Decode failed. Multi byte character was truncated.";
      }
      c2 = data[i+1];
      c3 = data[i+2];
      result += String.fromCharCode( ((c&15)<<12) | ((c2&63)<<6) | (c3&63) );
      i += 3;
    }
  }
  return result;
}

0

有一种使用 BlobFileReader异步方式。

您可以指定任何有效的编码。

function arrayBufferToString( buffer, encoding, callback ) {
    var blob = new Blob([buffer],{type:'text/plain'});
    var reader = new FileReader();
    reader.onload = function(evt){callback(evt.target.result);};
    reader.readAsText(blob, encoding);
}

//example:
var buf = new Uint8Array([65,66,67]);
arrayBufferToString(buf, 'UTF-8', console.log.bind(console)); //"ABC"

你可能想要查看TextDecoder API,以了解一种同步的方法。 - Xan
谢谢。TextDecoder非常好,但仅受Firefox和Chrome支持。在IE和Safari中需要一个大的shim。 - cuixiping
这个问题涉及到一个 Chrome 应用程序,因此运行时显式为 Chrome。 - Xan

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