javascript js对象转字符串 - 在字符串和ArrayBuffers之间转换




js字符串转arraybuffer js数字转字符串 (17)

有没有一种普遍接受的技术可以有效地将JavaScript字符串转换为ArrayBuffers ,反之亦然? 具体来说,我希望能够将ArrayBuffer的内容写入localStorage并将其读回。


Answers

2016更新 - 五年之后,现在有新的内置方法可用于使用适当的编码在字符串和类型化数组之间进行转换。

TextEncoder

TextEncoder表示

TextEncoder接口表示一种特定方法的编码器,即特定的字符编码,如utf-8 iso-8859-2koi8cp1261gbk ,... 编码器将码流作为输入并发射字节流。

自上述内容改为注释:(同上)

注意:Firefox,Chrome和Opera曾经支持utf-8以外的编码类型(如utf-16,iso-8859-2,koi8,cp1261和gbk)。 从Firefox 48 [...],Chrome 54和Opera 41开始,除了utf-8以外,没有其他编码类型可用,以符合规格。

*) 更新的规格

在创建TextEncoder的实例后,它将接收一个字符串并使用给定的编码参数对其进行编码:

if (!("TextEncoder" in window)) 
  alert("Sorry, this browser does not support TextEncoder...");

var enc = new TextEncoder(); // always utf-8
console.log(enc.encode("This is a string converted to a Uint8Array"));

然后,您当然可以使用生成的.buffer上的.buffer参数将下层ArrayBuffer转换为其他视图(如果需要)。

只要确保字符串中的字符符合编码模式,例如,如果您在示例中使用UTF-8范围以外的字符,则它们将被编码为两个字节而不是一个。

对于一般用途,您可以使用UTF-16编码来处理localStorage

TextDecoder

同样,相反的过程使用TextDecoder

TextDecoder接口表示特定方法的解码器,即特定字符编码,如utf-8iso-8859-2koi8cp1261gbk ,...解码器将字节流作为输入并发出代码点流。

所有可用的解码类型可以在here找到。

if (!("TextDecoder" in window))
  alert("Sorry, this browser does not support TextDecoder...");

var enc = new TextDecoder("utf-8");
var arr = new Uint8Array([84,104,105,115,32,105,115,32,97,32,85,105,110,116,
                          56,65,114,114,97,121,32,99,111,110,118,101,114,116,
                          101,100,32,116,111,32,97,32,115,116,114,105,110,103]);
console.log(enc.decode(arr));

MDN StringView库

另一种方法是使用StringView (许可为lgpl-3.0),其目标是:

  • 为基于JavaScript ArrayBuffer接口的字符串(即字符代码数组 - JavaScript中的ArrayBufferView)创建类C接口
  • 创建一个高度可扩展的库,任何人都可以通过向对象StringView.prototype添加方法来进行扩展
  • 为这种类似字符串的对象创建一个方法集合(从现在开始:stringViews),它严格依赖数组数组而不是创建新的不可变JavaScript字符串
  • 使用除JavaScript的默认UTF-16 DOMStrings之外的Unicode编码

给予更多的灵活性。 但是,在现代浏览器中内置TextEncoder / TextDecoder ,它需要我们链接或嵌入此库。


是:

const encstr = (`TextEncoder` in window) ? new TextEncoder().encode(str) : Uint8Array.from(str, c => c.codePointAt(0));

与此处的解决方案不同,我需要转换为UTF-8数据/从UTF-8数据转换。 为此,我使用(un)escape /(en)decodeURIComponent技巧编码了以下两个函数。 它们非常浪费内存,分配长度为编码utf8字符串长度的9倍,尽管这些应该由gc恢复。 只是不要将它们用于100MB文本。

function utf8AbFromStr(str) {
    var strUtf8 = unescape(encodeURIComponent(str));
    var ab = new Uint8Array(strUtf8.length);
    for (var i = 0; i < strUtf8.length; i++) {
        ab[i] = strUtf8.charCodeAt(i);
    }
    return ab;
}

function strFromUtf8Ab(ab) {
    return decodeURIComponent(escape(String.fromCharCode.apply(null, ab)));
}

检查它的工作原理:

strFromUtf8Ab(utf8AbFromStr('latinкирилицаαβγδεζηあいうえお'))
-> "latinкирилицаαβγδεζηあいうえお"


您可以使用由stringencoding库 TextDecoder编码标准中的 TextEncoderTextDecoder将字符串转换为ArrayBuffers或从ArrayBuffers转换字符串:

var uint8array = new TextEncoder(encoding).encode(string);
var string = new TextDecoder(encoding).decode(uint8array);

我发现我有这种方法的问题,基本上是因为我试图将输出写入文件,并且它没有正确编码。 由于JS似乎使用UCS-2编码( sourcesource ),我们需要进一步扩展这个解决方案,这是我的增强型解决方案。

我对通用文本没有任何困难,但是当它归结为阿拉伯语或韩语时,输出文件没有所有字符,而是显示错误字符

文件输出: ","10k unit":"",Follow:"Õ©íüY‹","Follow %{screen_name}":"%{screen_name}U“'Õ©íü",Tweet:"ĤüÈ","Tweet %{hashtag}":"%{hashtag} 'ĤüÈY‹","Tweet to %{name}":"%{name}U“xĤüÈY‹"},ko:{"%{followers_count} followers":"%{followers_count}…X \Ì","100K+":"100Ì tÁ","10k unit":"Ì è",Follow:"\°","Follow %{screen_name}":"%{screen_name} Ø \°X0",K:"œ",M:"1Ì",Tweet:"¸","Tweet %{hashtag}":"%{hashtag}

原文: ","10k unit":"万",Follow:"フォローする","Follow %{screen_name}":"%{screen_name}さんをフォロー",Tweet:"ツイート","Tweet %{hashtag}":"%{hashtag} をツイートする","Tweet to %{name}":"%{name}さんへツイートする"},ko:{"%{followers_count} followers":"%{followers_count}명의 팔로워","100K+":"100만 이상","10k unit":"만 단위",Follow:"팔로우","Follow %{screen_name}":"%{screen_name} 님 팔로우하기",K:"천",M:"백만",Tweet:"트윗","Tweet %{hashtag}":"%{hashtag}

我从http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String和我发现的这篇文章中获取信息。

这是我的代码:

function encode_utf8(s) {
  return unescape(encodeURIComponent(s));
}

function decode_utf8(s) {
  return decodeURIComponent(escape(s));
}

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

function str2ab(str) {
   var s = encode_utf8(str)
   var buf = new ArrayBuffer(s.length); 
   var bufView = new Uint8Array(buf);
   for (var i=0, strLen=s.length; i<strLen; i++) {
     bufView[i] = s.charCodeAt(i);
   }
   return bufView;
 }

这使我可以将内容保存到文件中而不会出现编码问题。

它是如何工作的:它基本上采用组成一个UTF-8字符的单个8字节块并将它们保存为单个字符(因此,以这种方式构建的UTF-8字符可由这些字符中的1-4个字符组成)。 UTF-8编码字符的长度从1到4个字节不等。 我们在这里执行的是在URI组件中对sting进行编码,然后获取该组件并将其转换为相应的8字节字符。 通过这种方式,我们不会丢失长度超过1个字节的UTF8字符所提供的信息。


atob()返回的“native”二进制字符串是每个字符1个字节的数组。

所以我们不应该将2个字节存储到一个字符中。

var arrayBufferToString = function(buffer) {
  return String.fromCharCode.apply(null, new Uint8Array(buffer));
}

var stringToArrayBuffer = function(str) {
  return (new Uint8Array([].map.call(str,function(x){return x.charCodeAt(0)}))).buffer;
}


基于gengkev的回答,我为两种方式创建了函数,因为BlobBuilder可以处理String和ArrayBuffer:

function string2ArrayBuffer(string, callback) {
    var bb = new BlobBuilder();
    bb.append(string);
    var f = new FileReader();
    f.onload = function(e) {
        callback(e.target.result);
    }
    f.readAsArrayBuffer(bb.getBlob());
}

function arrayBuffer2String(buf, callback) {
    var bb = new BlobBuilder();
    bb.append(buf);
    var f = new FileReader();
    f.onload = function(e) {
        callback(e.target.result)
    }
    f.readAsText(bb.getBlob());
}

一个简单的测试:

string2ArrayBuffer("abc",
    function (buf) {
        var uInt8 = new Uint8Array(buf);
        console.log(uInt8); // Returns `Uint8Array { 0=97, 1=98, 2=99}`

        arrayBuffer2String(buf, 
            function (string) {
                console.log(string); // returns "abc"
            }
        )
    }
)

我建议不要使用BlobBuilder等已弃用的API

BlobBuilder早已被Blob对象所弃用。 将Dennis的答案中的代码(其中使用BlobBuilder)与下面的代码进行比较:

function arrayBufferGen(str, cb) {

  var b = new Blob([str]);
  var f = new FileReader();

  f.onload = function(e) {
    cb(e.target.result);
  }

  f.readAsArrayBuffer(b);

}

请注意,与已弃用的方法相比,这有多清洁,少浮肿......是的,这绝对是要考虑的事情。


好吧,做同样的事情有点复杂:

var string = "Blah blah blah", output;
var bb = new (window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder)();
bb.append(string);
var f = new FileReader();
f.onload = function(e) {
  // do whatever
  output = e.target.result;
}
f.readAsArrayBuffer(bb.getBlob());

编辑: BlobBuilder一直被弃用,以支持Blob构造函数,这在我第一次写这篇文章时并不存在。 这是一个更新版本。 (是的,这一直是一个非常愚蠢的方式来做转换,但它只是为了好玩!)

var string = "Blah blah blah", output;
var f = new FileReader();
f.onload = function(e) {
  // do whatever
  output = e.target.result;
};
f.readAsArrayBuffer(new Blob([string]));

我用这个和为我工作。

function arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}



function base64ToArrayBuffer(base64) {
    var binary_string =  window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array( len );
    for (var i = 0; i < len; i++)        {
        bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
}

更新请看这个答案的后半部分,我有(希望)提供了一个更完整的解决方案。)

我也碰到过这个问题,FF 6中的以下作品(单向):

var buf = new ArrayBuffer( 10 );
var view = new Uint8Array( buf );
view[ 3 ] = 4;
alert(Array.prototype.slice.call(view).join(""));

不幸的是,当然,您最终会得到数组中值的ASCII文本表示,而不是字符。 不过,它仍然(应该)比循环更有效率。 例如。 对于上面的例子,结果是0004000000 ,而不是几个空字符和一个字符(4)。

编辑:

here查看MDC后,您可以按如下所示从Array创建一个ArrayBuffer

var arr = new Array(23);
// New Uint8Array() converts the Array elements
//  to Uint8s & creates a new ArrayBuffer
//  to store them in & a corresponding view.
//  To get at the generated ArrayBuffer,
//  you can then access it as below, with the .buffer property
var buf = new Uint8Array( arr ).buffer;

要回答你原来的问题,这可以让你转换ArrayBuffer < - > String ,如下所示:

var buf, view, str;
buf = new ArrayBuffer( 256 );
view = new Uint8Array( buf );

view[ 0 ] = 7; // Some dummy values
view[ 2 ] = 4;

// ...

// 1. Buffer -> String (as byte array "list")
str = bufferToString(buf);
alert(str); // Alerts "7,0,4,..."

// 1. String (as byte array) -> Buffer    
buf = stringToBuffer(str);
alert(new Uint8Array( buf )[ 2 ]); // Alerts "4"

// Converts any ArrayBuffer to a string
//  (a comma-separated list of ASCII ordinals,
//  NOT a string of characters from the ordinals
//  in the buffer elements)
function bufferToString( buf ) {
    var view = new Uint8Array( buf );
    return Array.prototype.join.call(view, ",");
}
// Converts a comma-separated ASCII ordinal string list
//  back to an ArrayBuffer (see note for bufferToString())
function stringToBuffer( str ) {
    var arr = str.split(",")
      , view = new Uint8Array( arr );
    return view.buffer;
}

为了方便起见,下面是一个将原始Unicode String转换为ArrayBufferfunction (仅适用于ASCII /单字节字符)

function rawStringToBuffer( str ) {
    var idx, len = str.length, arr = new Array( len );
    for ( idx = 0 ; idx < len ; ++idx ) {
        arr[ idx ] = str.charCodeAt(idx) & 0xFF;
    }
    // You may create an ArrayBuffer from a standard array (of values) as follows:
    return new Uint8Array( arr ).buffer;
}

// Alerts "97"
alert(new Uint8Array( rawStringToBuffer("abc") )[ 0 ]);

上面的代码允许你从ArrayBufferString再返回到ArrayBuffer ,在这里字符串可以存储在例如。 .localStorage :)

希望这可以帮助,


尽管Dennis和gengkev使用Blob / FileReader的解决方案的工作,我不会建议采取这种方法。 这是一个简单问题的异步方法,并且比直接解决方案慢得多。 我在html5rocks中发布了一个更简单的(更快)解决方案: http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String ://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String

解决方案是:

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;
}

编辑:

编码API有助于解决字符串转换问题。 查看上面的原始文章Html5Rocks.comJeff Posnik的回复。

摘抄:

Encoding API使得在原始字节和原生JavaScript字符串之间进行翻译变得简单,无论您需要使用多种标准编码中的哪一种。

<pre id="results"></pre>

<script>
  if ('TextDecoder' in window) {
    // The local files to be fetched, mapped to the encoding that they're using.
    var filesToEncoding = {
      'utf8.bin': 'utf-8',
      'utf16le.bin': 'utf-16le',
      'macintosh.bin': 'macintosh'
    };

    Object.keys(filesToEncoding).forEach(function(file) {
      fetchAndDecode(file, filesToEncoding[file]);
    });
  } else {
    document.querySelector('#results').textContent = 'Your browser does not support the Encoding API.'
  }

  // Use XHR to fetch `file` and interpret its contents as being encoded with `encoding`.
  function fetchAndDecode(file, encoding) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', file);
    // Using 'arraybuffer' as the responseType ensures that the raw data is returned,
    // rather than letting XMLHttpRequest decode the data first.
    xhr.responseType = 'arraybuffer';
    xhr.onload = function() {
      if (this.status == 200) {
        // The decode() method takes a DataView as a parameter, which is a wrapper on top of the ArrayBuffer.
        var dataView = new DataView(this.response);
        // The TextDecoder interface is documented at http://encoding.spec.whatwg.org/#interface-textdecoder
        var decoder = new TextDecoder(encoding);
        var decodedString = decoder.decode(dataView);
        // Add the decoded file's text to the <pre> element on the page.
        document.querySelector('#results').textContent += decodedString + '\n';
      } else {
        console.error('Error while requesting', file, this);
      }
    };
    xhr.send();
  }
</script>

玩过mangini的解决方案后,从ArrayBuffer转换为String - ab2str (这是我发现的最优雅最有用的一个 - 谢谢!),在处理大型数组时遇到了一些问题。 更具体地说,调用String.fromCharCode.apply(null, new Uint16Array(buf)); 抛出一个错误:

arguments array passed to Function.prototype.apply is too large

为了解决它(旁路),我决定分块处理输入的ArrayBuffer 。 所以修改后的解决方案是:

function ab2str(buf) {
   var str = "";
   var ab = new Uint16Array(buf);
   var abLen = ab.length;
   var CHUNK_SIZE = Math.pow(2, 16);
   var offset, len, subab;
   for (offset = 0; offset < abLen; offset += CHUNK_SIZE) {
      len = Math.min(CHUNK_SIZE, abLen-offset);
      subab = ab.subarray(offset, offset+len);
      str += String.fromCharCode.apply(null, subab);
   }
   return str;
}

块大小设置为2^16因为这是我发现在我的开发环境中工作的大小。 设置较高的值会导致同样的错误再次出现。 可以通过将CHUNK_SIZE变量设置为不同的值来更改它。 有一个偶数很重要。

关于性能的注意事项 - 我没有对此解决方案进行任何性能测试。 但是,由于它基于以前的解决方案,并且可以处理大型数组,我没有理由不使用它。

欢迎任何评论( - :


如果你使用巨大的数组例子arr.length=1000000你可以通过这段代码来避免堆栈回调问题

function ab2str(buf) {
var bufView = new Uint16Array(buf);
var unis =""
for (var i = 0; i < bufView.length; i++) {
    unis=unis+String.fromCharCode(bufView[i]);
}
return unis
}

从顶部反转功能mangini答案

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;
}

我们可以区分呼叫和应用方法如下

CALL:具有参数的函数单独提供。 如果你知道要传递的参数或者没有传递参数,你可以使用call。

APPLY:使用作为数组提供的参数调用函数。 如果您不知道有多少参数将传递给函数,则可以使用apply。

使用apply over call有一个优点,我们不需要更改参数的数量,只有我们可以更改传递的数组。

性能没有太大差异。 但是我们可以说调用比应用更快一点,因为数组需要在apply方法中进行评估。





javascript serialization arraybuffer typed-arrays