ファイル内の文字検索Webサイト作成に使った技術

# 本記事の目的
Javascript(Angular)で作った文字検索アプリのために使った技術の紹介

# 主要技術
- 正規表現を用いた文字の検索 - Javascript
- フォルダの読み込み - HTML
- 読み込んだファイルからテキストを読み込む - Javascript
- 任意の文字コードをUnicodeに変換する - Javascript
- ファイルのダウンロード - HTML

## 正規表現を用いた検索 - Javascript

RegExp.prototype.execメソッドを用いることで、テキストの何文字目に該当文字があるかを判定できます。
以下の例では、「嬉しい」という文字が文中にあるか、あるなら何番目かを知ることができます。

```javascript
let return ＝ /嬉しい/.exec("なんだか嬉しいな");

// return = [ '嬉しい', index: 4, input: 'なんだか嬉しいな' ]
-> return[0] = "嬉しい"
-> return.index = 4
-> return.input = "なんだか嬉しいな"
``` 
上のコードでは「嬉しい」という文字が4番目から始まるという結果が得られます。
 文字列が配列のため、0番から始まることに注意します。
<br>
また、execメソッドは再帰的に検索を行えます。
これは、execメソッドが一回目の検索結果を覚えていて次はその続きから検索できるからです。

```javascript
// Javascript
let return = /嬉しい/.exec("なんだか嬉しいな、なんか嬉しい");
``` 
上のコードを二回行った場合、一回目は4番目、2回目は12番目から始まるという結果が得られます。

この性質を利用して、ファイルから読み込んだテキスト内の検索文字を抽出しました。

## フォルダの読み込み - HTML
ファイル1枚の読み込みには、HTMLのinputタグのfileタイプを用います。
今回はフォルダの読み込みを行ったので、webkitdirectory属性を用いました。

```HTML
<!--HTML-->
<input type="file" webkitdirectory/>
```

Angularを使った場合の、読み込み時のイベントハンドリングの例も示します。

```HTML
<!--HTML-->
<input type="file" webkitdirectory (change)="onLoadFolda($event.target.files)" />
```
以上のコードではonLoadFoldaがハンドラーで$event.target.filesがファイル群になります。
**注意することは、$event.target.filesはオブジェクトで読み込まれることです。**
**{1: File, 2: File, ... n: File} という感じでkeyが数字で、valueがファイルの詳細です。**

## 読み込んだファイルからテキストを読み込む - Javascript
Javascriptには**FileReader API**という便利なものがあります[[2]](#参考文献)。
今回その中の、FileReaderモジュールとFileReader.prototype.readAsArrayBufferを使いました。
ここで、テキストではなくバッファーとして読み込んだのは、後で文字コードの変換を行うためです。

```ts

// Typescript
loadArrayBuffer(file: File): Promise<string | ArrayBuffer> {
  const reader = new FileReader();
  reader.readAsArrayBuffer(file);

  // 非同期の処理で、読み込みが終わるとreader.onloadの処理が走ります
  return new Promise((resolve, reject) => {
    reader.onload = () => { resolve(reader.result); };
    reader.onerror = () => { reject(reader.error); };
  });
}
```

## 任意の文字コードをUnicodeに変換する - Javascript
文字コードの変換のために、[encoding.js](https://github.com/polygonplanet/encoding.js/blob/master/README_ja.md)を用いました。文字コードを解析してくれる素晴らしいモジュールです。リンク先に使い方が書かれています。

今回の実装を以下に載せておきます。関数の内容を簡単に説明します。
関数の1行目では、引数で受け取ったArrayBufferをNode.jsのBuffer.fromメソッドを用いてBufferに変換しています。
2行目では、そのBufferの文字コードをconvert関数が読み取ってくれて、UNICODEに変換してくれます。
3行目では、codeToString関数を用いて、Bufferを文字列に変換して、それをreturnしています。

``` typescript
// Typescript
import { Buffer } from "buffer";
import * as encoding from "encoding-japanese";

convertArrayBufferToUnicodeString(arrayBuffer: ArrayBuffer): string {
  const buffer = Buffer.from(arrayBuffer);
  const charCodes = encoding.convert(buffer, "UNICODE");
  return encoding.codeToString(charCodes);
}
```

## ファイルのダウンロード - HTML
ファイルのダウンロードにはHTMLの<font color="MediumSlateBlue">アンカータグ</font>と<font color="MediumSlateBlue">href属性</font>を用います
ファイルをダウンロードするためのコードを以下に示します。

まず、HTMLを以下に示します。href属性はイベントハンドリングで動的に付与するので、HTMLにはありません。

```HTML
<!--HTML-->
<a [download]="saveFileName" [download]="保存先のファイル名" (click)="onDownload($event)"></a>
```

以下にイベントハンドラーonDownLoad($event)を示します。
1行目は出力したいデータです。
2行目は3行目のhref属性用のURLを作るために、Blobを作成しています。
3行目は、アンカータグ(event.currentTarget)のhref属性に、Blobから作ったURLを付与しています。

```typescript

onDownload(event: any) {
  const exportData = "保存する文字列";
  const blob: Blob = new Blob([exportData], { type: "application/x-msdownload" });
  event.currentTarget.href = window.URL.createObjectURL(blob);
}
```

# 参考文献
[1] [RegExp.prototype.exec | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec)
[2] [FileReader API | MDN](https://developer.mozilla.org/en-US/docs/Web/API/FileReader)