はじめに
ブラウザでCSVファイルを読み込み、文字列として取得する実装を解説します。
CSVのパース処理については次回の記事で扱います🌞
💡 この記事でわかること
- ブラウザでCSVファイルを読み込む方法
- 文字コードを自動検出・変換する方法
- BOMの除去方法
(実装コードは記事の最後にあります)
前回はCSVの仕様について書きました!
CSV読み込みの流れ
CSVファイルを読み込んで文字列として取得するまでの流れを以下のようにしました。
- ファイルをバイナリデータとして読み込む
- 文字コードを検出してUnicode文字列に変換する
- BOMがあれば除去する
ファイルデータをバイナリデータとして読み込む
FileとBlob
ブラウザで<input type="file">からファイルを取得すると、Fileオブジェクトが得られます。FileはBlobを継承したオブジェクトで、Blobのメソッドが使用できます。
ファイルの中身をバイナリデータとして取得するには、arrayBuffer()メソッドを使用します。
ArrayBufferとUint8Array
arrayBuffer()が返すArrayBufferは、バイナリデータを表すオブジェクトです。ArrayBufferの中身を直接操作することはできないため、バッファの中身を読み書きするには型付き配列オブジェクトを使用します。
今回は型付き配列オブジェクトの一つであるUint8Arrayを使用します。Uint8Arrayは、ArrayBufferの各バイトを0〜255の数値として扱います。
FileオブジェクトからArrayBufferを取得し、Uint8Arrayに変換します。
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
このUint8Arrayを次のセクションで解説するencoding-japaneseライブラリに渡します。
文字エンコーディングの変換
エンコーディングとは
文字エンコーディングとは、バイト列と文字を対応付けるものです。
同じバイト列でも、エンコーディングが異なれば異なる文字として解釈されます。
例えば、「あ」という文字は、エンコーディングによって以下のように異なるバイト列になります。
| エンコーディング | バイト列 |
|---|---|
| Shift_JIS | 130, 160 |
| UTF-8 | 227, 129, 130 |
| UTF-16(BE) | 48, 66 |
エンコーディングが不明な場合
ユーザーがどんな文字コードでCSVファイルを保存しているかを事前に知ることはできません。
例えば、Shift_JISでCSVを出力しても、ユーザーがExcelで編集し、「CSV UTF-8」形式で保存する可能性もあります。
そのため、アップロードされたファイルのエンコーディングを検出し、JavaScriptで扱えるUnicode文字列に変換する処理が必要です。
encoding-japaneseライブラリ
エンコーディングの検出と変換にはencoding-japaneseライブラリ(v2.2.0)を使用しました。
このライブラリを選んだ理由は、エンコーディングの自動検出機能を持っている点です。
ブラウザに標準で用意されているTextDecoderはShift_JISのデコードに対応していますが、エンコーディングを事前に指定する必要があります。
encoding-japaneseはエンコーディングを自動検出できるため、未知のエンコーディングのファイルを扱う用途に適しています。
Encoding.detect()
Encoding.detect()で文字コードを判定します。
const detectedEncoding = Encoding.detect(uint8Array);
if (!detectedEncoding) {
throw new Error('文字コードを判定できませんでした');
}
Shift_JIS、UTF-8などの対応する文字コードを検出できます。検出に失敗した場合はfalseを返すため、エラーを投げるようにしています。
Encoding.convert()
Encoding.convert()で文字コードを変換します。
-
to: 変換先の文字コード -
from: 変換元の文字コード
const unicodeArray = Encoding.convert(uint8Array, {
to: 'UNICODE',
from: detectedEncoding,
});
⚠️ encoding.js では JavaScript で扱える内部文字コード (JavaScript の文字列) のことを UNICODE と定義しています。
Encoding.codeToString()
Encoding.codeToString()で文字コードの数値配列を文字列に変換します。
const unicodeStr = Encoding.codeToString(unicodeArray);
BOMの除去
BOMとは
BOM(Byte Order Mark)は、Unicodeテキストファイルの先頭に付けることができる特殊な文字です。UTF-8では、バイト列EF BB BFがBOMとして使われます。
UTF-8の場合BOMは省略可能ですが、WindowsのExcelで「CSV UTF-8」形式で保存すると、ファイルの先頭にBOMが付きます。
BOMを除去する実装
BOM(U+FEFF)が先頭にあれば除去
const result =
unicodeStr.charCodeAt(0) === 0xfeff
? unicodeStr.slice(1)
: unicodeStr;
strip-bom パッケージと同様の方法でBOMを除去しています。
✍️ 補足1
Excelで「CSV UTF-8」形式で保存されないことが保証されている場合や、システム間連携でフォーマットが固定されている場合は、BOM除去の処理は不要です。
✍️ 補足2
BOMの表記
| BOM | 種別 |
|---|---|
EF BB BF |
UTF-8での表記 |
U+FEFF |
Unicodeでの表記 |
0xFEFF |
16進数リテラル(Javascript等) |
実装コード
ファイル読み込み → 文字コード変換 → BOM除去の一連の処理です。
import Encoding from 'encoding-japanese';
/** CSVファイルを読み込んで文字列に変換 */
export const readCsvFile = async (file: File): Promise<string> => {
try {
const arrayBuffer = await file.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const detectedEncoding = Encoding.detect(uint8Array);
const unicodeArray = Encoding.convert(uint8Array, {
to: 'UNICODE',
from: detectedEncoding || 'SJIS',
});
const unicodeStr = Encoding.codeToString(unicodeArray);
return unicodeStr.charCodeAt(0) === 0xfeff
? unicodeStr.slice(1)
: unicodeStr;
} catch {
throw new Error('ファイルの読み込みに失敗しました。');
}
};
まとめ
-
File.arrayBuffer()でバイナリデータとして読み込む - encoding-japaneseで文字コードを自動検出し、JavaScriptで扱える文字列に変換する
- BOMが含まれている場合は除去する
参考