UTF-8のバイト列とJavaScriptの文字列型との間の変換方法。ブラウザでもNode.jsでも共通のコードで動く。
ググっても古いやり方が出てくることが多いので書く。
エンコード
function encodeUTF8(str) {
const encoder = new TextEncoder();
return encoder.encode(str);
}
// 使い方例
const bytes = encodeUTF8('アイウ');
// Uint8Array(9) [227, 130, 162, 227, 130, 164, 227, 130, 166]
戻り値はUint8Array
。
デコード
function decodeUTF8(buffer) {
const decoder = new TextDecoder();
return decoder.decode(buffer);
}
// 使い方例
const str = decodeUTF8(Uint8Array.from([227, 130, 162, 227, 130, 164, 227, 130, 166]));
// "アイウ"
引数はUint8Array
を含めたTypedArray
やArrayBuffer
など。
UTF-8以外のエンコーディング
デコードの場合はnew TextDecoder()
の引数にエンコーディングの名前を入れれば別のエンコーディングもデコードできる。使えるエンコーディングは規格で定められており、UTF-16BE、UTF-16LE、ISO-2022-JP、Shift_JISなどは使えることになっている(UTF-32は書いてないので使えないらしい?)。
エンコードの場合はUTF-8しか扱えない。どうやらこれからの文字データ交換に使うエンコーディングは全てUTF-8であるべきという思想があるようだ。なのでUTF-8以外で表現された文字列はこれ以上生成できないようにしようということだろう。
const decoder = new TextDecoder('Shift_JIS');
// ↓これは働かない!
// const encoder = new TextEncoder('Shift_JIS');
Uint8Array
や ArrayBuffer
って何?
これらはバイナリ列を表現するためのJavaScript標準の型である。Uint8Array
は連続するメモリ領域を1バイトずつに区切り、区切った各要素内の1バイトを0から255までの符号なし整数として解釈した配列である(Pythonで言うbytes
型と同じ)。 同様な型は他にも複数あり、まとめてTypedArray
と呼称される。例えばInt16Array
は連続するメモリ領域を2バイトずつに区切り、区切った各要素内の2バイトを-32768から32767までの符号付き整数として解釈した配列である。
一方のArrayBuffer
はというと、単なる連続したメモリ領域を表す型と説明できる。そのデータ列の解釈については何も仮定しないのでArrayBufferはそのままでは内容を取得したり変更したりの操作はできない。読み書きをしたい場合はTypeArrayの各型等に通すことで、その型を通じて操作できる。
const buffer = new ArrayBuffer(12); // 12バイトの連続するメモリ領域を確保
const bytes = new Uint8Array(buffer); // メモリ領域を1バイト符号なし整数を要素とする配列として解釈
bytes[3] = 193; // bufferの4バイト目を193にセット
console.log(bytes[3]);
// 193
const int32Array = new Int32Array(buffer); // 同じメモリ領域を4バイト符号付き整数を要素とする配列として解釈
console.log(int32Array[0]); // bufferの1から4バイト目を符号付き整数として解釈した場合の値を読む
// -1056964608