JavaScriptでバイナリーを文字にデコードする

Author:

この記事では、JavaScriptでバイナリーを文字にデコードします。
バイナリーファイルがテキスト情報を含んでいる場合、バイナリーファイル内に含まれるテキスト部分を読むことができれば、テキストデータをもとにバイナリーデータを探すことができるため便利です。バイナリーだけでなく、テキストについても理解を深めましょう😀。



文字

HTMLは、過去、Shift_JISが主流でしたが、現在ではUTF-8での文字エンコーディングが推奨されています。
JavaScriptは、文字コードとしてUnicodeを採用し、内部で文字列を扱う際の文字エンコーディングはUTF-16を採用しています。
この様にバイナリーを文字にデコードする際には、文字コードおよび文字エンコーディングに気を付ける必要があり、これらについて知っておくと良いでしょう😆。

  • 文字コードは、コンピューターに文字(文字、数字、区切り記号、空白文字などを含む)を理解させるための符号化システムです。
    各国では個別の言語が使用されているため、日本語のJIS漢字コードのように独自の文字コードが開発されました。しかし、全世界の言語に対応するために、Unicodeが最も利用される文字コードになりました。
  • 文字エンコーディング(文字符号化)は、バイナリー(バイトの並び)と文字を対応付けるものです。バイトの並びは文字としてさまざまに解釈できますが、特定のエンコーディング(UTF-8、UTF-16など)を設定することで、バイトの並びがどのように解釈されるか定めることができます。

ASCII

ASCII(American Standard Code for Information Interchange、アスキー)は、情報交換のために標準化された符号化文字集合で、特定の制御文字および印字可能文字を表すために一意の数字を割り当てたものです。
例えば、改行は数字の10(0x0a)に、大文字の「A」は数字の65(0x41)に、小文字の「a」は97(0x61)に対応しています。

  • 制御文字(0(0x00)から31(0x1f)および127(0x7f))は、表示するための文字ではなく、モニターやプリンターなどの機器を制御するために用いられます。テキストを制御する改行やタブが含まれます。
  • 印字可能文字(32(0x20)から126(0x7e))は、空白文字(32(0x20))ならびに(33(0x21)から126(0x7e))の半角のアルファベット、数字、句読点および記号となります。
ASCIIコード表

Unicode

Unicodeは、世界の様々な言語、書式、記号に番号を割り当てて定義した標準の文字コードです。
一つ一つの文字に番号を割り当てることで、どの言語が混ざっていても、コンピューターに保存、処理、伝送させるような文字エンコーディングを同じファイルやプログラムの中に作ることができます。
UTF-16といったエンコーディングも存在しますが、ウェブ上でもっとも一般的な文字エンコーディングとしてUTF-8が推奨されています。
Unicodeの文字集合の符号空間は0x0~0x10FFFF16です。
0x0から0xffffからなる最初の6万5536個のコードポイント(符号位置)を基本多言語面(Basic Multilingual Plane、BMP)と呼び、最もよく使う基本的な文字、記号のほとんどを含みます。

  • 異体字セレクター: 文字の自体をより詳細に指定するためのコードポイントです。U+E0100からU+E01EFのコードポイント(240個)が、漢字異体字シーケンス(IVS)として定義されています。

UTF-16

UTF-16(UCS Transformation Format 16)は、WindowsやJavaの内部で使われている文字エンコーディングです。1文字あたり16ビット(2バイト)の符号単位が1つまたは2つで符号化されます。
BMP(基本多言語面)内の文字(U+0000からU+D7FFおよびU+E000からU+FFFF)は、符号単位1つの16ビット(2バイト)で表されます。BMP以外の文字(U+10000からU+10FFFF)は、符号単位2つの32ビット(4バイト)で表されます。

  • BOM(Byte Order Mark): UTF-16は、UTF-16、UTF-16BE、UTF-16LEの3種類あります。UTF-16では、通常はファイルの先頭にBOMが付与されます。BOMとは、符号化したテキストの先頭につける数バイトのバイナリーデータで、通信やファイルの読み書き等、8ビット単位の処理でバイト順を識別するための印であり、データの先頭に付与されます。UTF-16では、BOMが0xfe、0xffならビッグエンディアン、0xff、0xfeならリトルエンディアンとして後に続く文書を処理します。
    UTF-16BEは、16ビット整数をビッグエンディアンで直列化し、UTF-16LEはリトルエンディアンで直列化します。UTF-16BE、UTF-16LEの場合は、BOMの付加は認められません。
  • サロゲートペア(Surrogate Pair、代用対): UTF-16では、2つの16ビット(2バイト)のコードポイントを使い、32ビット(4バイト)でU+010000からU+10FFFFの文字を表します。U+D800からU+DFFFのコードポイントを、サロゲートコードポイント(代用符号位置)と呼び、この領域は文字が収録されておらず、UTF-16以外のUTF-8、UTF-32でも使用されません。
    最初の16ビットユニットをハイサロゲート、二番目の16ビットをローサロゲートと呼び、ハイサロゲートはU+D800からU+DBFFの範囲、ローサロゲートはU+DC00からU+DFFFの範囲となります。連続した2つのハイサロゲートとローサロゲートのペアをサロゲートペアと呼びます。

サロゲートペアの使用例

// 符号位置(Code Point)をcodePoint、ハイサロゲートをhighSurrogate、ローサロゲートをlowSurrogateとする。
// デコード
codePoint = 0x10000 + (highSurrogate - 0xd800) * 0x400 + (lowSurrogate - 0xdc00);

// エンコード
highSurrogate = Math.floor((codePoint - 0x10000) / 0x400) + 0xd800;
lowSurrogate = (codePoint - 0x10000) % 0x400 + 0xdc00;

// 例1 デコード
let highSurrogate = 0xd842;
let lowSurrogate = 0xdfb7;
let codePoint = 0x10000 + (highSurrogate - 0xd800) * 0x400 + (lowSurrogate - 0xdc00);
console.log(codePoint, String.fromCodePoint(codePoint)); // 134071(0x20bb7) '𠮷'

// 例2 エンコード
let codePoint = 0x20bb7;
let highSurrogate = Math.floor((codePoint - 0x10000) / 0x400) + 0xd800;
let lowSurrogate = (codePoint - 0x10000) % 0x400 + 0xdc00;
console.log(highSurrogate, lowSurrogate); // 55362(0xd842) 57271(0xdfb7)

UTF-8

UTF-8(UCS Transformation Format 8)は、ウェブ上において最も一般的な文字エンコーディングです。
1文字あたり1~4バイトで表します。UTF-8は、ASCIIに対して後方互換性を持っており、1バイトで表現することができます。また、4バイトのシーケンスでは21ビット(0x1fffff)が上限となっており、すべての標準Unicode文字を表現することができます。
UTF-8はもともと8ビットを符号単位とするためBOM(バイト順マーク)は必要ありませんが、UTF-8であることが識別できるように、データの先頭に0xef、0xbb、0xbf(U+FEFFのUTF-8での表現)の3バイトが付与されることがあります。Wikipediaによると、UTF-8のBOMはバイト順を表すものではなく、UTF-8でのBOMの使用は非推奨です😑。

UTF-8のビットパターン

バイナリーを文字にデコードする

バイナリーを文字にデコードするには、Stringオブジェクトの静的メソッドである、String.fromCharCode()またはString.fromCodePoint()ならびに様々な文字エンコーディングを扱うEncoding APIのTextEncoder.decode()メソッドを使用します。
String.fromCharCode()およびString.fromCodePoint()を使用する際は、バイナリーからコードポイントを計算する必要があります。
UTF-8のバイナリーをデコードする場合は、TextDecoder.decode()の方が便利ですが、ArrayBuffer、DataViewまたはTypedArrayオブジェクトのいずれかで引数を指定する必要があり、バイナリーデータの取り扱いを理解していると良いでしょう😤。

  • String.fromCharCode(): UTF-16コードの並びを使って生成された文字列を返します。UTF-16コードを表す数値の範囲は0から65535(0xffff)までです。
  • String.fromCodePoint(): Unicodeコードポイントの並びを使って生成された文字列を返します。Unicodeコードポイントを表す数値の範囲は、0から0x10ffffまでです。
  • TextDecoder.decode(): 引数として渡されたバッファーからデコードしたテキストを含む文字列を返します。TextDecoderオブジェクトの引数でデコードする文字コードを定義します。

String.fromCharCode()の例

console.log(String.fromCharCode(0x41, 0x42, 0x43)); // 'ABC'
console.log(String.fromCharCode(0x3042, 0x3044, 0x3046)); // 'あいう'
// String.fromCharCode サロゲートペア
console.log(String.fromCharCode(0xd867, 0xde3d)); // '𩸽'
console.log(String.fromCharCode(0xd83d, 0xde00)); // '😀'
console.log(String.fromCharCode(0xd867) + String.fromCharCode(0xde3d)); // '𩸽'
console.log(String.fromCharCode(0xd83d) + String.fromCharCode(0xde00)); // '😀'
// U+FFFFを超えるコードポイント
console.log(String.fromCharCode(0x29e3d)); // '鸽' 2の桁は削除され無視される
console.log(String.fromCharCode(0x1f600)); // '' 1の桁は削除され無視される
// 異体字セレクタ―
console.log(String.fromCharCode(0x845b)); // '葛' 葛飾区の「葛」
console.log(String.fromCharCode(0x845b, 0xdb40, 0xdd00)); // '葛󠄀' 異体字 葛󠄀城市の「葛󠄀(下の部分がヒ)」

String.fromCodePoint()の例

console.log(String.fromCharCode(0x41, 0x42, 0x43)); // 'ABC'
console.log(String.fromCharCode(0x3042, 0x3044, 0x3046)); // 'あいう'
// サロゲートペア
console.log(String.fromCodePoint(0xd867, 0xde3d)); // '𩸽'
console.log(String.fromCodePoint(0xd83d, 0xde00)); // '😀'
// U+FFFFを超えるコードポイント
console.log(String.fromCodePoint(0x29e3d)); // '𩸽'
console.log(String.fromCodePoint(0x1f600)); // '😀'
// 異体字セレクタ―
console.log(String.fromCodePoint(0x845b)); // '葛' 葛飾区の「葛」
console.log(String.fromCodePoint(0x845b, 0xe0100)); // '葛󠄀' 異体字 葛󠄀城市の「葛󠄀(下の部分がヒ)」

TextDecoder.decode()の例

// UTF-8
console.log(new TextDecoder().decode(new Uint8Array([0x41, 0x42, 0x43]))); // 'ABC'
console.log(new TextDecoder().decode(new Uint8Array([0xe3, 0x81, 0x82, 0xe3, 0x81, 0x84, 0xe3, 0x81, 0x86]))); // 'あいう'
console.log(new TextDecoder().decode(new Uint8Array([0xf0, 0xa9, 0xb8, 0xbd]))); // '𩸽'
console.log(new TextDecoder().decode(new Uint8Array([0xf0, 0x9f, 0x98, 0x80]))); // '😀'
console.log(new TextDecoder().decode(new Uint8Array([0xe8, 0x91, 0x9b]))); // '葛'
console.log(new TextDecoder().decode(new Uint8Array([0xe8, 0x91, 0x9b, 0xf3, 0xa0, 0x84, 0x80]))); // '葛󠄀'

// UTF-16 (ビッグエンディアン)
// TextDecoder()のラベルは'utf-16be'を指定する。'utf-16'はリトルエンディアンのUTF-16となる。
console.log(new TextDecoder('utf-16be').decode(new Uint8Array([0x00, 0x41, 0x00, 0x42, 0x00, 0x43]))); // 'ABC'
console.log(new TextDecoder('utf-16be').decode(new Uint8Array([0x30, 0x42, 0x30, 0x44, 0x30, 0x46]))); // 'あいう'
console.log(new TextDecoder('utf-16be').decode(new Uint8Array([0xd8, 0x67, 0xde, 0x3d]))); // '𩸽'
console.log(new TextDecoder('utf-16be').decode(new Uint8Array([0xd8, 0x3d, 0xde, 0x00]))); // '😀'
console.log(new TextDecoder('utf-16be').decode(new Uint8Array([0x84, 0x5b]))); // '葛'
console.log(new TextDecoder('utf-16be').decode(new Uint8Array([0x84, 0x5b, 0xdb, 0x40, 0xdd, 0x00]))); // '葛󠄀'

// UTF-16 (リトルエンディアン)
// TextDecoder()のラベルは'utf-16'または'utf-16le'を指定する。
console.log(new TextDecoder('utf-16').decode(new Uint8Array([0x41, 0x00, 0x42, 0x00, 0x43, 0x00]))); // 'ABC'
console.log(new TextDecoder('utf-16').decode(new Uint8Array([0x42, 0x30, 0x44, 0x30, 0x46, 0x30]))); // 'あいう'
console.log(new TextDecoder('utf-16').decode(new Uint8Array([0x67, 0xd8, 0x3d, 0xde]))); // '𩸽'
console.log(new TextDecoder('utf-16').decode(new Uint8Array([0x3d, 0xd8, 0x00, 0xde]))); // '😀'
console.log(new TextDecoder('utf-16').decode(new Uint8Array([0x5b, 0x84]))); // '葛'
console.log(new TextDecoder('utf-16').decode(new Uint8Array([0x5b, 0x84, 0x40, 0xdb, 0x00, 0xdd]))); // '葛󠄀'

バイナリーを文字にデコードするJavaScriptの例

このJavaScriptの例では、ファイルを選択すると、バイナリーデータから「アドレス」、「16進数データ」、「UTF-8文字列」を含む、テキストファイルを生成します。
ファイル上の先頭からのバイト位置を示すアドレス順に16進数で表示したバイナリーデータを並べるとバイナリーファイルを読むことができます。またバイナリーデータを変換した16進数テキストは、16バイト(32文字)ごとに区切るとデータの位置示すオフセット位置を調べる時に便利です。
テキストファイルでは文字コードとしてUTF-8が一般的に使用されているため、この記事ではバイナリーデータをUTF-8でデコードしたテキストを表示します。バイナリーの値からコードポイントを求めて、String.fromCodePoint()を使用して文字にデコードします。
ポイントとなる部分はソース内にコメントで記載しています。ソースが複雑になるためサロゲートペアおよび異体字セレクターを使用する文字へのデコードは行いません。

バイナリーファイルを16進数テキストに変換するJavaScript

完成例

入力: バイナリーファイル


ソース

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>バイナリーを文字にデコードした後、テキストファイルとして保存するJavaScript</title>
  </head>
  <body>
    入力: バイナリーファイル<br />
    <input type="file" id="input" />
    <script>

      // @ts-check

      /**
        * 標準組み込みオブジェクト
        * @typeof {object} ArrayBuffer
        * @typeof {object} DataView
        * Web API
        * @typeof {object} Blob
        * @typeof {object} BlobPart
        * @typeof {object} FileList
        * @typeof {object} FileReader
        * @typeof {object} HTMLAnchorElement
        * @typeof {object} HTMLInputElement
        */

      //------------------------------------------------------------
      // ライブラリー
      //------------------------------------------------------------

      /**
        * バイナリーデータ(ArrayBufferオブジェクト)をテキスト(UTF-8)にデコード
        * @param {ArrayBuffer} arrayBuffer テキスト(UTF-8)にデコードするArrayBufferオブジェクトによるバイナリーデータ
        * @returns {string} バイト毎にUTF-8でデコードしたテキスト
        */
      function arrayBufferToUtf8Text(arrayBuffer) {
        // 出力用テキスト
        let utf8Text = '';
        /** @type {DataView} ArrayBufferViewを使用 */
        const dataView = new DataView(arrayBuffer);
        // バイト毎にコードポイントを計算
        for (let i = 0, li = arrayBuffer.byteLength; i < li; i ++) {
          if (dataView.getUint8(i) >= 0x00 && dataView.getUint8(i) <= 0x7f) {
            // 1 バイト文字
            // 制御文字(U+0000 ~ U+001F、U+007F、U+0080 ~ U+009F)は空白とする
            if ((dataView.getUint8(i) >= 0x00 && dataView.getUint8(i) <= 0x1f) || dataView.getUint8(i) === 0x7f || (dataView.getUint8(i) >= 0x80 && dataView.getUint8(i) <= 0x9f)) {
              utf8Text += ' ';
            } else {
              // ビット論理積(&)を使用し、UTF-8のビットパターンに従ってコードポイントを取得
              const codePoint = dataView.getUint8(i) & 0b01111111;
              // String.fromCodePointを使用し、文字にデコード
              const string = String.fromCodePoint(codePoint);
              if (string.length === 1) {
              // 文字データを取得できた場合は、デコードした文字を出力用テキストに追加
                utf8Text += string;
              } else {
              // 文字データを取得できない場合は、空白を出力用テキストに追加
                utf8Text += ' ';
              }
            }
          } else if (i + 1 < li && dataView.getUint8(i) >= 0xc2 && dataView.getUint8(i) <= 0xdf && dataView.getUint8(i + 1) >= 0x80 && dataView.getUint8(i + 1) <= 0xbf) {
            // 2 バイト文字
            const codePoint = (dataView.getUint8(i) & 0b00011111 ) * 2 ** 6 + (dataView.getUint8(i + 1) & 0b00111111);
            const string = String.fromCodePoint(codePoint);
            if (string.length === 1) {
              utf8Text += string;
            } else {
              utf8Text += ' ';
            }
          } else if (i + 2 < li && dataView.getUint8(i) >= 0xe0 && dataView.getUint8(i) <= 0xef && dataView.getUint8(i + 1) >= 0x80 && dataView.getUint8(i + 1) <= 0xbf && dataView.getUint8(i + 2) >= 0x80 && dataView.getUint8(i + 2) <= 0xbf) {
            // 3 バイト文字
            // 第1バイトがE0のときに第2バイトが80-9Fの範囲を取るものは冗長な符号化となるため許されない → 空白とする
            // 第1バイトがEDのときに第2バイトがA0以上となるものはサロゲートペアのための符号位置にあたり、UTF-8では許されない → 空白とする
            if (dataView.getUint8(i) === 0xe0 && dataView.getUint8(i + 1) >= 0x80 && dataView.getUint8(i + 1) <= 0x9f) {
              utf8Text += ' ';
            } else if (dataView.getUint8(i) === 0xed && dataView.getUint8(i + 1) >= 0xa0) {
              utf8Text += ' ';
            } else {
              const codePoint = (dataView.getUint8(i) & 0b00001111 ) * 2 ** 12 + (dataView.getUint8(i + 1) & 0b00111111) * 2 ** 6 + (dataView.getUint8(i + 2) & 0b00111111);
              const string = String.fromCodePoint(codePoint);
              if (string.length === 1) {
                utf8Text += string;
              } else {
                utf8Text += ' ';
              }
            }
          } else if (i + 3 < li && dataView.getUint8(i) >= 0xf0 && dataView.getUint8(i) <= 0xf4 && dataView.getUint8(i + 1) >= 0x80 && dataView.getUint8(i + 1) <= 0xbf && dataView.getUint8(i + 2) >= 0x80 && dataView.getUint8(i + 2) <= 0xbf && dataView.getUint8(i + 3) >= 0x80 && dataView.getUint8(i + 3) <= 0xbf) {
            // 4 バイト文字
            // 第1バイトがF0のときに第2バイトが80-8Fの範囲を取るものは冗長な符号化となるため許されない  → 空白とする
            // 第1バイトがF4のときに第2バイトが90以上となるものはUnicodeの範囲外となるため、UTF-8では許されない → 空白とする
            if (dataView.getUint8(i) === 0xf0 && dataView.getUint8(i + 1) >= 0x80 && dataView.getUint8(i + 1) <= 0xbf) {
              utf8Text += ' ';
            } else if (dataView.getUint8(i) === 0xf4 && dataView.getUint8(i + 1) >= 0x90) {
              utf8Text += ' ';
            } else {
              const codePoint = (dataView.getUint8(i) & 0b00000111 ) * 2 ** 18 + (dataView.getUint8(i + 1) & 0b00111111) * 2 ** 12 + (dataView.getUint8(i + 2) & 0b00111111) * 2 ** 6 + (dataView.getUint8(i + 3) & 0b00111111);
              const string = String.fromCodePoint(codePoint);
              if (string.length === 1 || string.length === 2) {
              // U+FFFFを超えるコードポイントは、lengthが2になる
                utf8Text += string;
              } else {
                utf8Text += ' ';
              }
            }
          } else {
            utf8Text += ' ';
          }
        }
        return utf8Text;
      }

      /**
        * バイナリーデータ(ArrayBufferオブジェクト)を16進数テキストに変換
        * @param {ArrayBuffer} arrayBuffer 16進数テキストに変換するArrayBufferオブジェクトによるバイナリーデータ
        * @returns {string} 変換された16進数テキスト
        */
      function arrayBufferToHexText(arrayBuffer) {
        // 出力用テキスト
        let hexText = '';
        /** @type {DataView} */
        const dataView = new DataView(arrayBuffer);
        for (let i = 0; i < arrayBuffer.byteLength; i ++) {
          hexText += dataView.getUint8(i).toString(16).padStart(2, '0');
        }
        return hexText;
      }

      /**
        * ファイルに保存
        * @param {(BlobPart[])} blobParts Arrayオブジェクトなどの反復可能オブジェクト
        * @param {string} type blobに格納されるデータのMIMEタイプ
        * @param {string} fileName ファイル名
        */
        function saveFile(blobParts, type, fileName) {
        /** @type {Blob} */
        const blob = new Blob(blobParts, {
          type: type
        });
        const blobUrl = URL.createObjectURL(blob);
        /** @type {HTMLAnchorElement} */
        const a = document.createElement('a');
        a.download = fileName;
        a.href = blobUrl;
        a.click();
        URL.revokeObjectURL(blobUrl);
      }

      //------------------------------------------------------------
      // イベント  type="file"のインプット要素でファイルを選択時
      //------------------------------------------------------------
      // ts-check エラー対策 変数を用意し、nullを排除し、HTMLElementではなくHTMLInputElementとする
      const htmlInputElement = /** @type {!HTMLInputElement} */ (document.getElementById('input'));
      /** @this HTMLInputElement */
      htmlInputElement.addEventListener('change', function() {

        //------------------------------------------------------------
        // ローカル変数の設定 変更不要
        //------------------------------------------------------------
        // ts-check エラー対策 変数を用意し、nullを排除し、FileListとして定義
        const fileList = /** @type {!FileList} */ (this.files);
        /** @type {FileReader} */
        const reader = new FileReader();
        // 出力用 拡張子を除去したファイル名を取得
        const FILE_NAME = fileList[0].name.replace(/^(.+)\..+$/, '$1');

        //------------------------------------------------------------
        // イベント  ファイル読み込み時
        //------------------------------------------------------------
        reader.addEventListener('load', function () {

          //------------------------------------------------------------
          // ローカル変数の設定 変更不要
          //------------------------------------------------------------
          // ts-check エラー対策 変数を用意し、stringおよびnullを排除し、ArrayBufferとする
          const arrayBuffer = /** @type {!ArrayBuffer} */ (reader.result);
          // バイナリーデータ(ArrayBufferオブジェクト)を16進数テキストに変換
          // 入力用16進数テキスト
          let inputHexText = arrayBufferToHexText(arrayBuffer);
          // バイナリーデータ(ArrayBufferオブジェクト)をテキスト(UTF-8)にデコード
          // 入力用UTF-8デコード済みテキスト
          let inputUtf8Text = arrayBufferToUtf8Text(arrayBuffer);
          // 最初の行
          // 出力用テキスト
          let outputText = '          00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F        0123456789ABCDEF\u000d\u000a';

          // アドレス + 16進数テキスト + UTF-8でデコードしたテキストを並べる
          // 16バイトごとに処理
          for (let i = 0; i < arrayBuffer.byteLength / 16; i ++) {
            // 16進数テキスト用
            let outputHexText = '';
            // UTF-8でデコードしたテキスト用
            let outputUtf8Text = '';
            // 1行分の16進数テキストとUTF-8でデコードしたテキストを作成
            for (let j = 0; j < 16; j ++) {
              outputHexText += inputHexText.slice(2 * (i * 16 + j), 2 * (i * 16 + j + 1)) ? inputHexText.slice(2 * (i * 16 + j), 2 * (i * 16 + j + 1)) + ' ' : '   ';
              outputUtf8Text += inputUtf8Text.slice(i * 16 + j, i * 16 + j + 1) ? inputUtf8Text.slice(i * 16 + j, i * 16 + j + 1) : ' ';
            }
            // アドレス + 16進数テキスト + UTF-8でデコードしたテキストを繋げて、1行分の出力テキストを作成
            outputText += `${ (i).toString(16).padStart(7, '0') }0  ${ outputHexText }       ${ outputUtf8Text }\u000d\u000a`;
          }

          // ファイルに保存
          saveFile([outputText], 'text/plain', FILE_NAME + '(HexText).txt');

        });

        reader.readAsArrayBuffer(fileList[0]);

      });

    </script>
  </body>
</html>

参照


関連情報


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です