文字列とArrayBuffer間の変換


264

JavaScript文字列をArrayBuffersに効率的に変換するための一般に受け入れられている手法はありますか、またはその逆にますか?具体的には、ArrayBufferの内容を書き込んlocalStorageだり、読み込んだりできるようにしたいと考えています。


1
私はこれに関する経験はありませんが、APIドキュメント(khronos.org/registry/typedarray/specs/latest)から判断Int8Array ArrayBufferViewすると、ブラケット表記を使用して文字をコピーしstring[i] = buffer[i]たり、その逆を行ったりすることができます。
FK82 2011

2
@ FK82は妥当なアプローチのように見えますが(Uint16ArrayJSの16ビット文字にsを使用)、JavaScript文字列は不変であるため、文字位置に直接割り当てることはできません。それでもString.fromCharCode(x)、の各値をUint16Array通常の値にコピーしてから、Arrayを呼び出す必要.join()がありArrayます。
kpozin

@kpozin:確かに、本当にそうは思わなかった。
FK82 2011

5
@kpozin最近のほとんどのJSエンジンでは、文字列の連結が最適化されており、使用するだけの方が安くなっていますstring += String.fromCharCode(buffer[i]);。文字列と型付き配列の間で変換するための組み込みメソッドがないのは奇妙に思えます。彼らはこのようなことが起こることを知っていなければなりませんでした。
ダウンロード

arrayBuffer.toString()は私にとってうまく機能しています。
citizen conn 2015

回答:


128

2016年の更新 -5年後の仕様には、適切なエンコーディングを使用して文字列と型付き配列の間で変換する新しいメソッド(以下のサポートを参照)があります。

TextEncoder

TextEncoder表す

TextEncoderインターフェースのような、特定の文字エンコーディングである特定の方法のためのエンコーダを示しutf-8iso-8859-2koi8cp1261gbk、... エンコーダは、コードポイントのストリームを入力として受け取り、バイトのストリームを送信します。

上記の記述以降の変更点:(同上)

注:Firefox、Chrome、Operaでは、utf-8以外のエンコードタイプ(utf-16、iso-8859-2、koi8、cp1261、gbkなど)がサポートされていました。Firefox 48 [...]、Chrome 54 [...]、Opera 41では、仕様に合わせるため、utf-8以外のエンコードタイプは使用できません。*

*)仕様(W3)とここ(whatwg )を更新しました

のインスタンスを作成した後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て、結果のパラメータを使用してUint8Array、アンダーレイArrayBufferを別のビューに変換します。

たとえば、文字列内の文字がエンコードスキーマに準拠していることを確認してください。たとえば、例でUTF-8の範囲外の文字を使用すると、1バイトではなく2バイトにエンコードされます。

一般的な用途では、UTF-16エンコーディングを使用しますlocalStorage

TextDecoder

同様に、反対のプロセスでは以下を使用しTextDecoderます。

TextDecoderインタフェースは、特定の文字エンコーディングである特定の方法、デコーダを表し、等utf-8iso-8859-2koi8cp1261gbk、...デコーダは、入力として、バイトのストリームを受け取り、コード・ポイントのストリームを放出します。

利用可能なすべてのデコードタイプはここにあります

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インターフェースに基づいて、文字列のCのようなインターフェース(つまり、文字コードの配列— JavaScriptのArrayBufferView)を作成する
  • オブジェクトStringView.prototypeにメソッドを追加することで誰でも拡張できる高度に拡張可能なライブラリを作成する
  • 新しい不変のJavaScript文字列の作成ではなく、数値の配列で厳密に機能する、このような文字列のようなオブジェクトのメソッドのコレクションを作成する(現在はstringViews)
  • JavaScriptのデフォルトのUTF-16 DOMStrings以外のUnicodeエンコーディングを使用するには

はるかに柔軟性を与えます。ただし、TextEncoder/ TextDecoderが最新のブラウザに組み込まれているときに、このライブラリにリンクするか、このライブラリを埋め込む必要があります。

サポート

2018年7月現在:

TextEncoder (実験的、標準トラック上)

 Chrome    | Edge      | Firefox   | IE        | Opera     | Safari
 ----------|-----------|-----------|-----------|-----------|-----------
     38    |     ?     |    19°    |     -     |     25    |     -

 Chrome/A  | Edge/mob  | Firefox/A | Opera/A   |Safari/iOS | Webview/A
 ----------|-----------|-----------|-----------|-----------|-----------
     38    |     ?     |    19°    |     ?     |     -     |     38

°) 18: Firefox 18 implemented an earlier and slightly different version
of the specification.

WEB WORKER SUPPORT:

Experimental, On Standard Track

 Chrome    | Edge      | Firefox   | IE        | Opera     | Safari
 ----------|-----------|-----------|-----------|-----------|-----------
     38    |     ?     |     20    |     -     |     25    |     -

 Chrome/A  | Edge/mob  | Firefox/A | Opera/A   |Safari/iOS | Webview/A
 ----------|-----------|-----------|-----------|-----------|-----------
     38    |     ?     |     20    |     ?     |     -     |     38

Data from MDN - `npm i -g mdncomp` by epistemex

2
IEおよびEdgeからのTextDecoderのサポートなし:caniuse.com/#search=TextDecoder
Andrei Damian-Fekete


2018-04-18のSafari Mobile(ios)はサポートされていません:developer.mozilla.org/en-US/docs/Web/API/TextDecoder
ブロンズマン

ワンライナー:var encoder = 'TextEncoder' in window ? new TextEncoder() : {encode: function(str){return Uint8Array.from(str, function(c){return c.codePointAt(0);});}};できるのでvar array = encoder.encode('hello');
イエティ2018

1
重要なのTextEncoderは、文字列(イメージなど)にバイナリデータがある場合、TextEncoder(明らかに)使用したくないということです。コードポイントが127より大きい文字は2バイトを生成します。文字列にバイナリデータがあるのはなぜですか?cy.fixture(NAME, 'binary')cypress)は文字列を生成します。
x-yuri

176

Blob / FileReaderを使用するDennisとgengkevのソリューションは機能しますが、そのアプローチを取ることはお勧めしません。これは、単純な問題に対する非同期アプローチであり、直接的なソリューションよりもはるかに低速です。私はhtml5rocksでシンプルで(はるかに高速な)ソリューションで投稿しました:http : //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.comのJeff 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>

16
残念ながら、html5rocksに関する私のコメントはまだ承認されていません。したがって、ここで簡単な答えを示します。まだ多くの文字が欠落しているため、特に現在ほとんどのページがUTF-8エンコーディングであるため、これは正しい方法ではないと私はまだ思います。一方では、より多くの特殊文字(たとえば、アジア文字)の場合、charCodeAt関数は4バイトの値を返すため、それらは切り取られます。反対に、単純な英語の文字はArrayBufferを2倍にします(1バイトの文字ごとに2バイトを使用しています)。WebSocketを介して英語のテキストを送信することを想像してみてください。
Dennis

9
3つの例:(1)This is a cool text!UTF8で20バイト-Unicodeで40バイト。(2)ÄÖÜUTF8で6バイト-Unicodeで6バイト (3)☐☑☒UTF8で9バイト-Unicodeで6バイト (BlobおよびFile Writer APIを介して)文字列をUTF8ファイルとして保存する場合、ArrayBufferはUTF8ではなくUnicodeになるため、この2つのメソッドを使用できません。
Dennis

3
エラーが発生する:Uncaught RangeError:呼び出しスタックの最大サイズを超えました。何が問題でしょうか?
ジェイコブ

6
@Dennis-JS文字列はUTF8(またはUTF16でもない)ではなくUCS2を使用します。つまり、charCodeAt()は常に値0-> 65535を返します。4バイトの終了が必要なUTF-8コードポイントはサロゲートペアで表されます(en.wikipediaを参照).org / wiki /…)-つまり、2つの別々の16ビットUCS2値。
ブローファ2013年

6
@jacob-エラーは、apply()メソッドに渡すことができる配列の長さに制限があるためだと思います。たとえばString.fromCharCode.apply(null, new Uint16Array(new ArrayBuffer(246300))).length、Chromeでは私のために機能しますが、代わりに246301を使用すると、RangeError例外が発生します
broofa

71

stringencoding ライブラリでポリフィルされているエンコード標準を使用TextEncoderTextDecoderて、文字列をArrayBufferとの間で変換できます。

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

2
ちなみに、これはFirefoxでデフォルトで利用可能です:developer.mozilla.org/en-US/docs/Web/API/TextDecoder.decode
Joel Richard

2
奇妙な回避策よりもはるかに優れた新しいAPIに賛成です!
トマーシュZato -復活モニカ

1
これは、そこにあるすべてのタイプのキャラクターでは機能しません。
David

5
npm install text-encodingvar textEncoding = require('text-encoding'); var TextDecoder = textEncoding.TextDecoder;。結構です。
Evan Hu

grumble ...既存のarraybufferがある場合、文字列を書き込みたいので、uint8arrayを2回目にコピーする必要があると思いますか?
ショーン

40

Blobは、 String.fromCharCode(null,array);

しかし、配列バッファが大きくなりすぎると失敗します。私が見つけた最良の解決策は使用することですString.fromCharCode(null,array);それして、スタックを爆破しないような操作に分割することですが、一度に1つの文字より高速です。

大きな配列バッファーの最適なソリューションは次のとおりです。

function arrayBufferToString(buffer){

    var bufView = new Uint16Array(buffer);
    var length = bufView.length;
    var result = '';
    var addition = Math.pow(2,16)-1;

    for(var i = 0;i<length;i+=addition){

        if(i + addition > length){
            addition = length - i;
        }
        result += String.fromCharCode.apply(null, bufView.subarray(i,i+addition));
    }

    return result;

}

これはblobを使用するよりも約20倍速いことがわかりました。また、100 MBを超える大きな文字列でも機能します。


3
私たちはこの解決策をとるべきです。受け入れられたものよりも、この解き1以上のユースケースとして
SAM

24

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"
            }
        )
    }
)

arrayBuffer2String()で、console.log()の代わりにcallback(...)を呼び出すつもりでしたか?それ以外の場合、コールバック引数は使用されません。
Dan Phillimore、2012年

これは進むべき道のようです-genkevとDennisに感謝します。そこにこれを達成するための何の同期方法ませんが、あなたは何を行うことができます種類愚かなのようです...
kpozin

JavaScriptはシングルスレッドです。したがって、FileReaderは2つの理由で非同期です:(1)(巨大な)ファイルのロード中に他のJavaScriptの実行をブロックしません(より複雑なアプリケーションを想像してください)および(2)UI /ブラウザーをブロックしません(一般的な問題)実行時間の長いJSコード)。多くのAPIは非同期です。XMLHttpRequest 2でも同期は削除されます。
デニス

これがうまくいくことを本当に望んでいましたが、文字列からArrayBufferへの変換が確実に機能していません。私は256の値を持つArrayBufferを作成しており、それを長さ256の文字列に変換できます。しかし、それをArrayBufferに変換しようとすると、最初のArrayBufferの内容に応じて、376要素が得られます。私の問題を再現したい場合は、ArrayBufferをUint8Arrayの16x16グリッドとして扱います。値a[y * w + x] = (x + y) / 2 * 16;getBlob("x")、試したように計算されたもので、さまざまなMIMEタイプで運がありません。
Matt Cruikshank、2012年

18
BlobBuilderは新しいブラウザでは非推奨です。に変更new BlobBuilder(); bb.append(buf);new Blob([buf])、2番目の関数のArrayBufferを介してUintArray new UintArray(buf)(または、基になるデータ型に適切なもの)にキャストしてから、getBlob()呼び出しを取り除きます。最後に、きれいにするために、bbの名前をblobに変更します。これは、BlobBuilderではなくなったためです。
sowbug

18

以下はすべて、配列バッファからバイナリ文字列を取得することに関するものです

使用しないことをお勧めします

var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));

それは

  1. 大きなバッファでクラッシュする(誰かが246300の「マジック」サイズについて書いたが、Maximum call stack size exceeded、120000バイトのバッファ(Chrome 29)でエラー発生した)
  2. パフォーマンス非常に低い(下記参照)

同期ソリューションが正確に必要な場合は、次のようなものを使用してください

var
  binaryString = '',
  bytes = new Uint8Array(arrayBuffer),
  length = bytes.length;
for (var i = 0; i < length; i++) {
  binaryString += String.fromCharCode(bytes[i]);
}

前のものと同じくらい遅いですが、正しく動作します。これを書いている現時点では、その問題に対する非常に高速な同期ソリューションはないようです(このトピックで言及されているすべてのライブラリは、同期機能に同じアプローチを使用しています)。

しかし、私が本当にお勧めするのはBlob+ FileReaderアプローチの使用です

function readBinaryStringFromArrayBuffer (arrayBuffer, onSuccess, onFail) {
  var reader = new FileReader();
  reader.onload = function (event) {
    onSuccess(event.target.result);
  };
  reader.onerror = function (event) {
    onFail(event.target.error);
  };
  reader.readAsBinaryString(new Blob([ arrayBuffer ],
    { type: 'application/octet-stream' }));
}

唯一の欠点(すべてではない)は非同期であること。また、以前のソリューションよりも約8〜10倍高速です。(一部の詳細:私の環境での同期ソリューションは2.4Mbバッファーで950-1050ミリ秒かかりましたが、FileReaderを使用したソリューションは同じ量のデータで約100-120ミリ秒かかりました。また、100Kbバッファーで両方の同期ソリューションをテストしました。ほぼ同じ時間なので、「apply」を使用してもループはそれほど遅くありません。)

ところでここで:ArrayBufferをString作者に変換する方法と作者から変換する方法は、私のような2つのアプローチを比較し、完全に反対の結果を得ます(彼のテストコードはこちら)おそらく、彼のテスト文字列が1Kbであることが原因です(彼はそれを "veryLongStr"と呼びました)。私のバッファは、サイズが2.4Mbの非常に大きなJPEG画像でした。


13

更新この回答の後半を参照してください。ここでは、(できれば)より完全なソリューションを提供しています。)

私もこの問題に遭遇しました。FF6で次のことがうまくいきました(一方向):

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

残念ながら、もちろん、文字ではなく配列の値をASCIIテキストで表現することになります。ただし、ループよりもはるかに効率的です(そうする必要があります)。例えば。上記の例の場合、結果は0004000000、いくつかのnull文字とchr(4)ではなくになります。

編集:

ここMDC を調べた後、以下からを作成できます。ArrayBufferArray

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

便宜上、function未加工のUnicode Stringをに変換するためのは次のとおりですArrayBuffer(ASCII / 1バイト文字でのみ機能します)

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 ]);

上記では、文字列が格納されているArrayBuffer-> String&からArrayBuffer再び戻ることができます。.localStorage:)

お役に立てれば、

ダン


1
これは(時間またはスペースの点で)効率的な方法ではないと思います。これは、バイナリデータを格納する非常に珍しい方法です。
kpozin

@kpozin:私の知る限り、localStorageにバイナリデータを格納する方法は他にありません
Dan Phillimore

1
base64エンコーディングの使用についてはどうですか?
Nick Sotiros、2015年

13

ここでのソリューションとは異なり、私はUTF-8データとの間で変換を行う必要がありました。この目的のために、(un)escape /(en)decodeURIComponentトリックを使用して、次の2つの関数をコーディングしました。エンコードされたutf8-stringの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кирилицаαβγδεζηあいうえお"

8

文字列にバイナリデータがある場合(nodejs+ readFile(..., 'binary')cypress+ cy.fixture(..., 'binary')などから取得)、は使用できませんTextEncoder。のみサポートしていutf8ます。値>= 128を持つバイトはそれぞれ2バイトに変換されます。

ES2015:

a = Uint8Array.from(s, x => x.charCodeAt(0))

Uint8Array(33)[2、134、140、186、82、70、108、182、233、40、143、247、29、76、245、206、29、87、48、160、78、225、242 、56、236、201、80、80、152、118、92、144、48

s = String.fromCharCode.apply(null, a)

"ºRFl¶é(÷LõÎW0Náò8ìÉPPv\ 0"


7

このアプローチに問題があることがわかりました。基本的には、出力をファイルに書き込もうとしていて、適切にエンコードされていなかったためです。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}

私はデニスの解決策と私が見つけたこの投稿から情報を得ました。

これが私のコードです:

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コンポーネントで文字列をエンコードし、このコンポーネントを取得して、対応する8バイト文字に変換しています。このようにして、1バイトを超える長さのUTF8文字によって提供される情報を失うことはありません。


6

巨大な配列の例を使用した場合、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
}

上から逆関数 マンジーニ答え

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

4

まあ、これは同じことを行うためのやや複雑な方法です:

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]));

3

変換するためのmanginiの溶液を用いて再生した後ArrayBufferString- 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変数を別の値に設定することで変更できます。偶数にすることが重要です。

パフォーマンスに関するメモ-このソリューションのパフォーマンステストは行いませんでした。ただし、これは前のソリューションに基づいており、大きな配列を処理できるため、使用しない理由はわかりません。


typedarray.subarrayを使用して、指定した位置とサイズでチャンクを取得できます。これは、jsでバイナリ形式のヘッダーを読み取るために行うことです
Nikos M.


2
  stringToArrayBuffer(byteString) {
    var byteArray = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
      byteArray[i] = byteString.codePointAt(i);
    }
    return byteArray;
  }
  arrayBufferToString(buffer) {
    var byteArray = new Uint8Array(buffer);
    var byteString = '';
    for (var i = 0; i < byteArray.byteLength; i++) {
      byteString += String.fromCodePoint(byteArray[i]);
    }
    return byteString;
  }

文字列にUnicode文字が含まれている場合、このコードにはバグがあります。例:arrayBufferToString(stringToArrayBuffer('🐴'))==='44'
xmcp

2

node.jsおよびhttps://github.com/feross/bufferを使用するブラウザーの場合

function ab2str(buf: Uint8Array) {
  return Buffer.from(buf).toString('base64');
}
function str2ab(str: string) {
  return new Uint8Array(Buffer.from(str, 'base64'))
}

注:ここでの解決策は私にとってはうまくいきませんでした。node.jsとブラウザーをサポートし、UInt8Arrayを文字列にシリアル化する必要があります。私はそれを数値としてシリアル化することもできます[]が、それは不必要なスペースを占有します。そのソリューションでは、base64であるため、エンコーディングについて心配する必要はありません。他の人が同じ問題に苦しんでいる場合に備えて...私の2セント


2

あなたがarrayBuffer binaryStrを持っているとしましょう:

let text = String.fromCharCode.apply(null, new Uint8Array(binaryStr));

次に、テキストを状態に割り当てます。


1

atob()が返す「ネイティブ」バイナリ文字列は、文字ごとに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;
}


0

BlobBuilderなどの廃止されたAPIを使用しないことをお勧めします

BlobBuilderは長い間、Blobオブジェクトによって非推奨になりました。BlobBuilderが使用されているDennisの回答のコードと以下のコードを比較してください。

function arrayBufferGen(str, cb) {

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

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

  f.readAsArrayBuffer(b);

}

これが非推奨の方法と比べてどれほどきれいで肥大化していないかに注意してください。


つまり、そうですが、そのBlobコンストラクタは2012年には実際には使用できませんでした;)
gengkev '16 / 10/16


0

私はこれを使用して私のために働いています。

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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.