便利ページ：Javascriptでバイナリファイル操作

[便利ページ：Javascriptでちょっとした便利な機能を作ってみた](https://qiita.com/poruruba/items/6c67c7eef34ed302feb3) のシリーズものです。

今回は、Javascsript上でバイナリファイルを開いたり、バイナリファイルに保存したりしてみます。
他の有志のページで、たくさんの情報がありましたので、非常に助かりました。

毎度の通り、デモページとGitHubです。

GitHub
　https://github.com/poruruba/utilities

デモページ
　https://poruruba.github.io/utilities/

# バイナリファイルを開く

HTMLのtype="file"のinput要素を使います。
このinputのボタンによりファイルを選択されたイベントをフックし、あとはHTML5のFile機能を使います。

```html:index.html
                <input type="file" v-on:change="binary_open" v-on:click="binary_click"><br>
                <button v-on:click="binary_save()">ファイルに保存</button><br>
                <br>
                <div class="form-inline">
                    <label>改行</label>
                    <select class="form-control" v-model="binary_cr_num">
                        <option value="0">無し</option>
                        <option value="2">2バイト</option>
                        <option value="4">4バイト</option>
                        <option value="8">8バイト</option>
                        <option value="16">16バイト</option>
                    </select>
                    <input type="checkbox" v-model="binary_space">空白　
                    <button class="btn btn-primary" v-on:click="binary_cr()">整形</button><br>
                </div>
                <textarea class="form-control" rows="5" v-model="binary_output"></textarea>
```

type="file"のファイルが指定されると、Vueの構文でv-on:changeで指定した関数（binary_open）が呼ばれます。
ArrayBufferとして読み出して、読み出しが完了したときに、16進数文字列にして表示させています。

```js:start.js
        binary_open: function(e){
            var file = e.target.files[0];
            var reader = new FileReader();
            reader.onload = (theFile) =>{
                this.binary_output = byteAry2hexStr(new Uint8Array(theFile.target.result));
            };
            reader.readAsArrayBuffer(file);
        },
        binary_click: function(e){
            e.target.value = '';
        },
```

一方、v-on:clickで関数(binary_click)を指定しています。
これは、一度ファイルを選択した後に、再度同じファイルを選択してもonchangeが呼ばれないため、ファイル選択ボタンを押したときに、以前に選択していたファイルを未選択状態にしています。

# バイナリファイルを保存

バイナリファイルの保存は、以下の関数です。

```js:start.js
        binary_save: function(){
            var target = this.binary_output.replace(/\r?\n|\s/g, '');
			var array = hexStr2byteAry(target);
            var buffer = new ArrayBuffer(array.length);
			var dv = new DataView(buffer);
			for( var i = 0 ; i < array.length ; i++ )
				dv.setUint8(i, array[i]);

			var blob = new Blob([buffer], {type: "octet/stream"});
			var url = window.URL.createObjectURL(blob);

			var a = document.createElement("a");
			a.href = url;
			a.target = '_blank';
			a.download = "array.bin";
			a.click();
			window.URL.revokeObjectURL(url);
        },
```

最初に、改行と空白を削除しています。それは後述する整形を想定してのことです。整形を実行したときに改行やスペースを挿入して表示しているためです。

# 16進数文字列を整形する

長いバイト列を16進数文字列で表示させると見にくくなります。
そこで、適当に改行を入れたり、1バイトごとに空白を入れたりしています。例えば、16バイトごとに改行を入れるなど。

```js:start.js
        binary_cr: function(){
            var target = this.binary_output.replace(/\r?\n|\s/g, '');
			var array = hexStr2byteAry(target);
            var num_of_interval = Number(this.binary_cr_num);
            if( num_of_interval == 0 ){
                if( this.binary_space )
                    this.binary_output = byteAry2hexStr(array, ' ');
                else
                    this.binary_output = byteAry2hexStr(array);
            }else{
                var str = '';
                for( var i = 0 ; i < array.length ; i += num_of_interval ){
                    if( this.binary_space )
                        str += byteAry2hexStr(array.slice( i, i + num_of_interval ), ' ') + '\n';
                    else
                        str += byteAry2hexStr(array.slice( i, i + num_of_interval )) + '\n';
                }
                this.binary_output = str;
            }
        },
```

以上