0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CSV文字コード変換ツールはどう動くのか。Shift_JISとUTF-8をブラウザ内で扱う仕組み

0
Posted at

CSV を扱うツールを作っていると、かなりの頻度でぶつかるのが文字コードの問題です。

「UTF-8 の CSV を Excel で開いたら文字化けした」
「取引先から送られてきた Shift_JIS の CSV をシステムに入れたら壊れた」

このあたりは日本語の実務だと本当によくあります。

そこで今回は、ぱんだツールズCSV文字コード変換ツール を題材に、ブラウザ内でどう動いているのか を整理してみます。

やること自体は単純で、流れとしては次の 3 段階です。

  • CSV ファイルをバイト列として読む
  • そのバイト列がどの文字コードかを判定、または指定してデコードする
  • 目的の文字コードで再エンコードしてダウンロードさせる

仕組みを分解すると、意外と難しくありません。

そもそも何が問題なのか。CSVは見た目が同じでも中身のバイト列が違う

CSV はただのテキストファイルですが、同じ「あいうえお」と表示されていても、内部のバイト列は文字コードによって変わります。

例えば代表的なものはこのあたりです。

  • UTF-8
  • Shift_JIS
  • EUC-JP
  • UTF-16

Web 開発では UTF-8 前提で進むことが多いですが、日本の業務フローでは今でも Shift_JIS の CSV がかなり残っています。

そのため、単に FileReader で文字列として読めばよいわけではありません。
まずは ファイルを生のバイト列として扱う 必要があります。

基本の流れ。最初に文字列ではなく ArrayBuffer として読む

ブラウザで CSV を受け取る場合、input type="file" かドラッグ&ドロップで File オブジェクトを取得します。

ここで最初からテキストとして読むと、文字コードの解釈をブラウザ任せにしてしまうので危険です。

まずはこういう流れで、ArrayBuffer として読みます。

async function readFileAsBytes(file: File): Promise<Uint8Array> {
  const buffer = await file.arrayBuffer();
  return new Uint8Array(buffer);
}

この段階では、まだ「文字」ではなく単なるバイト列です。

例えば Shift_JIS の CSV なら、日本語 1 文字が 1 バイトまたは 2 バイトで表現されます。
UTF-8 なら 1 文字あたりのバイト数は別のルールになります。

つまり、まず必要なのは「このバイト列をどのルールで文字に戻すか」を決めることです。

文字コード変換ツールの肝。デコードとエンコードを分けて考える

文字コード変換は、頭の中で次の 2 ステップに分けると整理しやすいです。

  1. バイト列を文字列に戻す
  2. 文字列を別の文字コードのバイト列に変換する

前半を デコード、後半を エンコード と考えます。

たとえば Shift_JIS の CSV を UTF-8 に変換するなら、

  1. Shift_JIS のバイト列を Unicode 文字列として解釈する
  2. その Unicode 文字列を UTF-8 のバイト列に変換する

という流れです。

この「いったん内部では文字列に戻す」という段階を挟むのがポイントです。

判定はどうやるのか。自動判定は万能ではない

実装上は「文字コードを自動判定したい」と考えがちですが、ここは少し注意が必要です。

文字コードの自動判定は、厳密にはかなり難しいです。
特に短い CSV や、数字・ASCII 文字が多い CSV は判定材料が少なくなります。

例えば次のようなファイルは判定が難しくなります。

  • ヘッダーが英数字だけ
  • 中身が日付や数値中心
  • 日本語が数行しか入っていない

なので実務向けのツールでは、だいたい次のどちらかにします。

  • BOM やバイトパターンを見て自動推定する
  • ユーザーに元の文字コードを選ばせる

実際には、自動推定を補助として使いつつ、最終的には選択肢を出す ほうが安全です。

ブラウザ実装でよくある構成。encoding-japaneseのようなライブラリを使う

JavaScript 標準の TextDecoder / TextEncoder でも UTF 系の処理はやれますが、Shift_JIS を含む日本語の実務向け変換では対応が足りないことがあります。

そのため、ブラウザ完結で作るなら encoding-japanese のようなライブラリを使う構成が実用的です。

イメージとしては次のような流れです。

import Encoding from "encoding-japanese";

function decodeCsv(bytes: Uint8Array, from: Encoding.Encoding): string {
  const unicodeArray = Encoding.convert(bytes, {
    to: "UNICODE",
    from,
    type: "array",
  });

  return Encoding.codeToString(unicodeArray);
}

function encodeCsv(text: string, to: Encoding.Encoding): Uint8Array {
  const unicodeArray = Encoding.stringToCode(text);

  return new Uint8Array(
    Encoding.convert(unicodeArray, {
      to,
      from: "UNICODE",
      type: "array",
    })
  );
}

やっていることはシンプルで、

  • 入力ファイルのバイト列を Unicode 文字列に戻す
  • その文字列を目的の文字コードへ変換する

だけです。

見た目には「CSV を変換している」ように見えますが、実際に変換しているのは CSV の構造そのものではなくテキストのエンコーディング です。

CSVツールとしては、区切り文字や改行コードも一緒に面倒を見ると使いやすい

厳密には文字コード変換だけでもツールとして成立します。
ただ、実際の運用では次も同時に問題になることが多いです。

  • カンマ区切りではなくタブ区切りになっている
  • 改行コードが LFCRLF で食い違う
  • ダブルクオートを含むセルの扱いが壊れる

なので、実務で使いやすいツールにするなら、

  • 文字コード変換
  • 改行コード変換
  • CSV パースと再生成

を分けて考えるのが大事です。

単にバイト列を文字列へ戻して再エンコードするだけなら高速ですが、列の整形や値の検査までやるなら Papa Parse のような CSV パーサーも併用することになります。

ダウンロードはどうやるのか。Blob を作ってその場で保存させる

変換後のデータは、ブラウザ上で Blob にしてダウンロードさせるのが一般的です。

function downloadBytes(bytes: Uint8Array, filename: string) {
  const blob = new Blob([bytes], { type: "text/csv" });
  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  a.click();

  URL.revokeObjectURL(url);
}

これならサーバーにファイルを送らず、そのままローカルで完結できます。

業務ファイルを扱うツールでは、この「アップロードしなくてよい」はかなり大きな価値があります。

ブラウザ完結にするメリット。プライバシーと運用コストの両方に効く

CSV の文字コード変換は、サーバー側でやることももちろんできます。

ただ、ブラウザ内で完結させると次の利点があります。

  • 機密データを外部へ送らずに済む
  • 小さな変換なら体感が速い
  • サーバー負荷と保存リスクを減らせる
  • 個人開発でも運用コストを抑えやすい

特に CSV は顧客情報や請求データを含むことが多いので、「変換のために外部アップロードが必要です」は思った以上に心理的ハードルがあります。

実装でハマりやすいポイント

最後に、文字コード変換ツールを作るときにハマりやすい点を挙げます。

1. UTF-8 with BOM をどう扱うか

Excel 向けでは、UTF-8 でも BOM 付きのほうが都合がよいことがあります。

そのため「UTF-8 に変換」で終わりではなく、

  • UTF-8
  • UTF-8 with BOM

を分けて出せるようにすると実務では親切です。

2. 文字化けした後のファイルは元に戻せないことがある

一度誤った文字コードで保存し直された CSV は、元の情報が壊れていることがあります。

つまり、変換ツールで救えるのは 元データのバイト列がまだ正しい場合 です。
すでに壊れた文字列を再変換しても完全には戻らないケースがあります。

3. 巨大ファイルはブラウザメモリを圧迫する

ブラウザ完結は便利ですが、数百 MB 級の CSV を読むとメモリ使用量が増えます。

小中規模の実務ファイルなら十分実用的ですが、巨大データ向けにはストリーム処理やサーバー処理を検討したほうがよいです。

まとめ

CSV文字コード変換ツールの中身は、突き詰めると次の流れです。

  1. ファイルを ArrayBuffer として読む
  2. 元の文字コードでデコードする
  3. 目的の文字コードでエンコードする
  4. Blob としてダウンロードさせる

派手さはありませんが、日本語の実務ではかなり価値のあるツールです。

特に Shift_JIS と UTF-8 の行き来は、いまでも現場で普通に発生します。
もしブラウザ完結の小さな業務ツールを作ってみたいなら、CSV 文字コード変換はかなり良い題材だと思います。

実際に試したい場合は、こちらから使えます。

ぱんだツールズ では他にも PDF・画像・CSV・テキスト処理などの開発者向けツールを 50 個以上公開しています。全部無料・登録不要・ブラウザ完結で使えます。
https://sakutto-panda.com


この記事は Zenn にも同じ内容を投稿しています。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?