何をしたいユーティリティか:「文字コード判定」
ここでの「文字コード判定」は、外部から受け取った「バイト列」が、UTF-8 なのか Shift_JIS なのか EUC-JP なのか、といった「エンコーディング(文字コード)」を推測する処理です。
ブラウザや Node.js の中で扱う文字列は基本的に「JavaScript の内部表現(UTF-16 系)」ですが、ファイルや HTTP レスポンスを「バイト列」として受け取るときは、どの文字コードで書かれているかを知らないと正しく文字列に変換できません。
まず前提を整理する:文字列とバイト列
JavaScript の「文字列」はすでにデコード済み
"あいう" のような JavaScript の文字列は、すでに「文字コードが解釈された後」の世界です。
つまり、「この文字列が Shift_JIS か UTF-8 か」を判定することはできませんし、そもそも意味がありません。
文字コード判定の対象になるのは「バイト列(Uint8Array や ArrayBuffer)」です。
判定したいのは「バイト列 → 文字列」に変換する前
典型的な流れはこうです。
- ファイルやレスポンスをバイト列として受け取る。
- そのバイト列の文字コードを推測する。
- 推測した文字コードを使って「バイト列 → JavaScript 文字列」に変換する。
この「2. 文字コードを推測する」が、ここで作りたいユーティリティの役割です。
実務では「自前で全部やろうとしない」が正解
文字コード判定は本質的に難しい
UTF-8、Shift_JIS、EUC-JP、ISO-2022-JP…
日本語を扱う文字コードだけでも複数あり、バイト列だけを見て「これは絶対に Shift_JIS」と言い切るのは、かなり難しい問題です。
そのため、実務では「実績のあるライブラリを使う」のが現実的です。
encoding-japanese(encoding.js)のようなライブラリ
有名どころとして、encoding-japanese(encoding.js)というライブラリがあります。
このライブラリは、バイト配列から文字コードを推測する detect 関数と、文字コード変換を行う convert 関数を提供しており、ブラウザでも Node.js でも使えます。 Github GeSource
ライブラリを使った「文字コード判定」ユーティリティ
基本的な使い方のイメージ
encoding-japanese を読み込んだ前提で、バイト列の文字コードを判定するユーティリティを作ってみます。
// 例: Encoding は encoding-japanese のグローバル or import 済みオブジェクト
// const Encoding = require("encoding-japanese");
function detectEncoding(bytes) {
if (!bytes) {
return null;
}
const arr = bytes instanceof Uint8Array ? Array.from(bytes) : bytes;
const detected = Encoding.detect(arr);
return detected || null;
}
JavaScriptここでは、Uint8Array でも普通の配列でも受け取れるようにしつつ、
内部的には「数値配列([0xE3, 0x81, …] のような形)」にそろえています。
判定結果を使って文字列に変換する
判定した文字コードを使って、バイト列を JavaScript 文字列に変換するユーティリティもセットで用意すると便利です。
function decodeWithAutoDetect(bytes) {
if (!bytes) {
return "";
}
const arr = bytes instanceof Uint8Array ? Array.from(bytes) : bytes;
const encoding = Encoding.detect(arr);
const unicodeArray = Encoding.convert(arr, {
from: encoding,
to: "UNICODE",
});
return Encoding.codeToString(unicodeArray);
}
JavaScriptこれで、「バイト列を渡すだけで、文字コードを自動判定して文字列にしてくれる」関数になります。
TextDecoder と「判定済みの文字コード」
TextDecoder は「判定」ではなく「デコード」
ブラウザや Node.js には TextDecoder という標準 API があり、new TextDecoder("utf-8") のように「文字コード名を指定して」バイト列を文字列に変換できます。
const decoder = new TextDecoder("utf-8");
const text = decoder.decode(uint8Array);
JavaScriptここで重要なのは、TextDecoder は「文字コードを判定してくれるわけではない」という点です。
「すでに分かっている文字コード名」を渡して使うものなので、「判定」と組み合わせるなら、
- ライブラリなどで文字コードを推測する。
- 推測結果を TextDecoder に渡してデコードする。
という流れになります。
自前でやる「超ざっくり判定」の例(考え方の練習用)
UTF-8 っぽいかどうかだけを見る簡易チェック
本番で使うには心もとないですが、「文字コード判定のイメージ」をつかむために、
「このバイト列が UTF-8 として妥当かどうか」をざっくりチェックする関数を書いてみます。
function looksLikeUtf8(bytes) {
const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
let i = 0;
while (i < arr.length) {
const b = arr[i];
if (b <= 0x7f) {
i += 1;
} else if (b >= 0xc2 && b <= 0xdf) {
if (i + 1 >= arr.length) return false;
const b2 = arr[i + 1];
if (b2 < 0x80 || b2 > 0xbf) return false;
i += 2;
} else if (b >= 0xe0 && b <= 0xef) {
if (i + 2 >= arr.length) return false;
const b2 = arr[i + 1];
const b3 = arr[i + 2];
if (b2 < 0x80 || b2 > 0xbf || b3 < 0x80 || b3 > 0xbf) return false;
i += 3;
} else if (b >= 0xf0 && b <= 0xf4) {
if (i + 3 >= arr.length) return false;
const b2 = arr[i + 1];
const b3 = arr[i + 2];
const b4 = arr[i + 3];
if (
b2 < 0x80 || b2 > 0xbf ||
b3 < 0x80 || b3 > 0xbf ||
b4 < 0x80 || b4 > 0xbf
) {
return false;
}
i += 4;
} else {
return false;
}
}
return true;
}
JavaScriptこれは「UTF-8 のバイト列として矛盾がないか」をチェックしているだけで、
「UTF-8 ではない」=「Shift_JIS だ」とまでは言えません。
ただ、「UTF-8 っぽいなら UTF-8 とみなす」「そうでなければ別のエンコーディング候補を試す」といった戦略の一部として使えます。
実務で意識してほしい設計のポイント
1. 「何を判定したいか」を絞る
「世界中のすべての文字コードを自動判定したい」と考えると、途端に難易度が跳ね上がります。
業務システムでは、まず「自分たちが実際に扱う可能性のある文字コード」を絞るのが現実的です。
例としては、次のような方針が多いです。
- 原則 UTF-8。
- 例外的に Shift_JIS(古いシステムや Excel 由来の CSV など)。
- EUC-JP や ISO-2022-JP は必要なら追加で対応。
この「候補セット」が決まれば、ライブラリの判定結果をその中にマッピングするだけで済みます。
2. 「判定に失敗したとき」の扱いを決める
文字コード判定は確率的なものなので、「どうしても判定できない」「誤判定する」ケースは避けられません。
そのときにどう振る舞うかを、あらかじめ決めておくことが大事です。
例えば、
- 判定できなければ UTF-8 とみなす。
- 判定できなければエラーにして、ユーザーに文字コードを選ばせる。
など、システムの性質に合わせてルールを決めておきます。
3. 「判定」と「変換」をユーティリティに閉じ込める
アプリケーションのあちこちで、
const enc = Encoding.detect(bytes);
const unicode = Encoding.convert(bytes, { from: enc, to: "UNICODE" });
JavaScriptのようなコードを書き始めると、
あとから仕様を変えたいときに全体を直す必要が出てきます。
そうではなく、
export function detectEncoding(bytes) { ... }
export function decodeWithAutoDetect(bytes) { ... }
JavaScriptのようなユーティリティを 1 箇所に置き、
「文字コードに関することは必ずここを通す」と決めておくと、
仕様変更にも強く、コードも読みやすくなります。
少し手を動かして感覚をつかむ
ブラウザのコンソールや Node.js で、次のようなコードを試してみてください(Encoding が使える前提)。
// UTF-8 の「これはテストです。」に相当するバイト列
const utf8 = Uint8Array.from([
0xE3, 0x81, 0x93, 0xE3, 0x82, 0x8C, 0xE3, 0x81, 0xAF,
0xE3, 0x83, 0x86, 0xE3, 0x82, 0xB9, 0xE3, 0x83, 0x88,
0xE3, 0x81, 0xA7, 0xE3, 0x81, 0x99, 0xE3, 0x80, 0x82,
]);
detectEncoding(utf8);
decodeWithAutoDetect(utf8);
looksLikeUtf8(utf8);
JavaScriptdetectEncoding の結果、decodeWithAutoDetect の文字列、looksLikeUtf8 の真偽値を見比べて、
「バイト列 → 文字コード判定 → 文字列」という流れのイメージをつかんでみてください。
そのうえで、自分のプロジェクトに
export function detectEncoding(...) { ... }
export function decodeWithAutoDetect(...) { ... }
export function looksLikeUtf8(...) { ... } // 必要なら
JavaScriptのような関数を置き、
「外部から来たバイト列に触るときは、必ず“文字コード判定ユーティリティ”を通す」
というルールを作ってみてください。
それだけで、あなたのシステムの文字コード周りは、「なんとなく動いている」状態から、意図と一貫性を備えた業務レベルの設計に近づいていきます。

