JavaScript文字列を ArrayBuffers に、またはその逆に効率的に変換するための一般的に認められている手法はありますか?具体的には、ArrayBufferの内容をlocalStorage
に書き込み、それを読み返すことができればと思います。
2016年更新-5年後には、適切なエンコーディングを使用して文字列と型付き配列を変換する新しいメソッドが仕様に追加されました(以下のサポートを参照)。
TextEncoder
インターフェイスは、特定のメソッドのエンコーダー、つまりutf-8
などの特定の文字エンコードを表します。エンコーダーは入力としてコードポイントのストリームを受け取り、バイトのストリームを出力します。iso-8859-2
、koi8
、cp1261
、gbk
、...
上記の記述以来、メモを変更してください:(同上)
注: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バイトにエンコードされます。
一般的な用途では、localStorage
のようなものにUTF-16エンコーディングを使用します。
同様に、反対のプロセス TextDecoder
を使用 :
TextDecoder
インターフェイスは、特定のメソッドのデコーダー、つまりutf-8
、iso-8859-2
、koi8
、cp1261
、gbk
などの特定の文字エンコードを表します。バイトストリームを入力として受け取り、コードポイントのストリームを出力します。
利用可能なすべてのデコードタイプは 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));
これらの代替方法は、 StringView
library (lgpl-3.0としてライセンスされています)を使用することです。
- javaScript ArrayBufferインターフェースに基づいて、文字列(つまり、文字コードの配列— JavaScriptのArrayBufferView)のCライクなインターフェースを作成する
- 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
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;
}
編集:
Encoding 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>
ArrayBuffersとの間で文字列を変換するには、 stringencodingライブラリ で埋められている 符号化規格 のTextEncoder
とTextDecoder
を使用できます。 :
var uint8array = new TextEncoder().encode(string);
var string = new TextDecoder(encoding).decode(uint8array);
ブロブはString.fromCharCode(null,array);
よりずっと遅い
しかし、配列バッファが大きくなりすぎると失敗します。私が見つけた最善の解決策は、String.fromCharCode(null,array);
を使って、スタックを飛ばさないが一度に1つのcharより速い操作に分割することです。
大規模配列バッファーの最善の解決策は、次のとおりです。
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倍高速であることがわかりました。 100MBを超える大きな文字列でも機能します。
BlobBuilder はStringとArrayBufferを処理できるので、gengkevの答えに基づいて、両方の方法で関数を作成しました。
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"
}
)
}
)
使用しないことをお勧めします
var binaryString = String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));
それだから
Maximum call stack size exceeded
エラーが発生しました(Chrome 29))。あなたが正確に同期的な解決策を必要とするならば、のようなものを使ってください
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ミリ秒の時間がありました。そして私はbothをテストしました100Kbバッファ上の同期式の解決策とほぼ同じ時間がかかるため、 'apply'を使用してもループが遅くなることはありません。)
ここでところで: どのようにStringとの間でArrayBufferを変換するか 著者は私のような2つのアプローチを比較して全く反対の結果を得ます( 彼のテストコードはこちら )どうしてこんなに違う結果なの?おそらく1Kbの長さのテスト文字列が原因です(彼はそれを "veryLongStr"と呼びました)。私のバッファは2.4Mbのサイズの本当に大きなJPEG画像でした。
ここでの解決策とは異なり、私はUTF-8データとの間で変換する必要がありました。この目的のために、(un)escape /(en)decodeURIComponentトリックを使用して、次の2つの関数をコーディングしました。それらはgcによって回復されるべきですが、それらはエンコードされたutf8文字列の9倍の長さを割り当てて、メモリのかなり無駄です。 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кирилицаαβγδεζηあいうえお"
(更新この答えの後半を見てください。そこでは、私が(できれば)もっと完全な解決策を提供しています。)
私はこの問題にも遭遇しました、FF 6(私のための1つの方向のために)のために以下の作品:
var buf = new ArrayBuffer( 10 );
var view = new Uint8Array( buf );
view[ 3 ] = 4;
alert(Array.prototype.slice.call(view).join(""));
残念ながら、もちろん、文字ではなく、配列内の値のASCIIテキスト表現になります。それでも、ループよりもはるかに効率的です。例えば。上記の例では、結果はいくつかのnull文字とa chr(4)ではなく0004000000
です。
編集:
ここでMDCを見て 、次のようにArrayBuffer
からArray
を作成できます。
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のfunction
を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
:)
お役に立てれば、
ダン
基本的に出力をファイルに書き込もうとしていたが、正しくエンコードされていなかったため、この方法に問題があることがわかりました。 JSはUCS-2エンコーディングを使用しているようです( source 、 source )私たちはこの解決策をさらに一歩拡張する必要があります。これが私に役立つ私の強化された解決策です。
私は一般的なテキストで問題はありませんでしたが、アラビア語または韓国語になったとき、出力ファイルにはすべての文字が含まれておらず、代わりにエラー文字が表示されていました
ファイル出力:","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}
私は dennisの解決法 と この記事 から情報を得ました。
これが私のコードです:
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バイトを超えるUTF-8文字によって与えられた情報を失うことはありません。
まあ、これは同じことをやや複雑にした方法です:
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]));
ES2015:
a=Uint8Array.from(s,(x)=>x.charCodeAt(0))
Uint8配列(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"
巨大な配列の例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;
}
ArrayBuffer
からString
- ab2str
への変換のためのmanginiの解決策を試した後(これは私が見つけた最も洗練された便利なものです - ありがとう!)、大規模配列を処理するときにいくつか問題がありました。より具体的には、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
変数を別の値に設定することで変更できます。偶数を持つことが重要です。
パフォーマンスについての注意 - このソリューションのパフォーマンステストは行いませんでした。しかし、これは以前のソリューションに基づいており、大規模な配列を処理できるため、使用しない理由はわかりません。
任意のコメントは大歓迎です( - :
こちらを参照してください。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays/StringView (JavaScriptに基づく文字列用のCライクなインタフェース) ArrayBufferインタフェース)
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;
}
Atob()が返す "ネイティブ"バイナリ文字列は、1文字あたり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;
}
私はこれを使って私のために働きます。
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;
}
はい:
const encstr = (`TextEncoder` in window) ? new TextEncoder().encode(str) : Uint8Array.from(str, c => c.codePointAt(0));
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);
}
これが廃止予定の方法と比較してどれほどクリーンで肥大化していないかに注意してください。うん、これは間違いなくここで考慮すべきものです。
var decoder = new TextDecoder ();
var string = decoder.decode (arrayBuffer);
https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode を参照してください。