【JavaScript】特定のエンコーディングのサポート有無を判定する方法|TextDecoderで安全にチェック

外部ファイルやレガシーシステムのデータを扱うとき、指定した文字エンコーディングがブラウザでサポートされているかを事前に判定したい場面があります。JavaScriptには対応エンコーディングの一覧を取得するAPIはありませんが、TextDecoder を使えば個別に「使えるかどうか」をチェックできます。

この記事の結論:new TextDecoder(エンコーディング名)try/catch で囲み、RangeError が出なければサポートされていると判定します。UTF-8Shift_JISEUC-JP などWHATWG Encoding Standard に載っているラベルはすべて対応しており、対応外の無効なラベルのときだけ例外になります。
スポンサーリンク

TextDecoder で判定する(基本)

TextDecoderサポートしていないエンコーディング名を渡すと RangeError を投げる仕様です。これを try/catch で捕まえれば、サポートの有無を真偽値で返せます。

isEncodingSupported 関数
function isEncodingSupported(encoding) {
  try {
    new TextDecoder(encoding);
    return true;
  } catch (e) {
    // 未対応のラベルだと RangeError になる
    return false;
  }
}

console.log(isEncodingSupported("UTF-8"));     // true
console.log(isEncodingSupported("Shift_JIS")); // true
console.log(isEncodingSupported("EUC-JP"));    // true
console.log(isEncodingSupported("Big5"));      // true
console.log(isEncodingSupported("foo-123"));   // false(無効なラベル)
EUC-JP は「サポートされている(true)」です:EUC-JP は WHATWG Encoding Standard の正式なラベルで、モダンブラウザ・Node.js の TextDecoderすべて対応しています。false になるのは "foo-123" のような規格に存在しないラベルだけです。

ラベルは別名・大文字小文字を区別しない

エンコーディング名(ラベル)は大文字小文字を区別せず複数の別名(エイリアス)が認められています。判定後に TextDecoderencoding プロパティを見ると、正規化された名前が分かります。

別名と正規化
console.log(isEncodingSupported("sjis"));       // true(Shift_JIS の別名)
console.log(isEncodingSupported("shift-jis"));  // true

// 正規化された名前を確認
console.log(new TextDecoder("sjis").encoding);  // "shift_jis"
console.log(new TextDecoder("UTF-8").encoding); // "utf-8"

注意:TextEncoder は UTF-8 専用

混同しやすいのですが、エンコード側の TextEncoder は UTF-8 しか扱えません。引数にエンコーディングを渡しても無視され、常に UTF-8 になります。つまり「Shift_JIS でエンコードする」標準APIは存在しません(デコード=TextDecoder だけが多言語対応)。

TextEncoder は引数を無視
const enc = new TextEncoder("shift_jis");
console.log(enc.encoding); // "utf-8"(shift_jis は無視される)

console.log(new TextEncoder().encoding); // "utf-8"
このため「サポート判定」が意味を持つのは基本的にデコード(TextDecoder)側です。Shift_JIS や EUC-JP のバイト列を読み込んで文字列に変換する用途で使います。

実用例:Shift_JIS ファイルを読み込む

判定の典型的な使いどころが、Shift_JIS など非UTF-8のファイル読み込みです。FileReaderreadAsArrayBuffer() でバイト列を取得し、サポートを確認したうえで TextDecoder でデコードします。

FileReader + TextDecoder
function readShiftJIS(file) {
  const encoding = "shift_jis";
  if (!isEncodingSupported(encoding)) {
    throw new Error(encoding + " は未対応です");
  }
  const reader = new FileReader();
  reader.onload = () => {
    const bytes = new Uint8Array(reader.result);
    const text = new TextDecoder(encoding).decode(bytes);
    console.log(text); // UTF-16 文字列としてそのまま扱える
  };
  reader.readAsArrayBuffer(file);
}
バイト列を直接デコード(動作確認)
// Shift_JIS の「あ」は 0x82 0xA0
const bytes = new Uint8Array([0x82, 0xA0]);
const text = new TextDecoder("shift_jis").decode(bytes);
console.log(text); // "あ"

不正なバイトを検出する(fatal オプション)

「ラベルが対応しているか」とは別に、実際のバイト列がそのエンコーディングとして正しいかを厳密にチェックしたいこともあります。{ fatal: true } を付けると、不正なバイトで例外を投げるため、エンコーディング推定の判定材料に使えます。

fatal で不正バイトを検出
const bytes = new Uint8Array([0xff, 0xfe, 0x00]);

// 既定(非fatal)は置換文字に置き換えてエラーにしない
console.log(new TextDecoder("utf-8").decode(bytes)); // 文字化け(置換文字)

// fatal:true は不正バイトで例外を投げる
try {
  new TextDecoder("utf-8", { fatal: true }).decode(bytes);
} catch (e) {
  console.log(e.name); // "TypeError"
}

まとめて確認:判定のポイント

やりたいこと 書き方
ラベルが対応しているか try { new TextDecoder(label) } catch { ... }
正規化された名前 new TextDecoder(label).encoding
バイト列が正しいか new TextDecoder(label, { fatal: true })
エンコード(出力) new TextEncoder()(UTF-8固定)

よくある質問(FAQ)

Q特定のエンコーディングがサポートされているか確認するには?
Anew TextDecoder(エンコーディング名)try/catch で囲み、RangeError が発生しなければサポートされていると判定します。UTF-8Shift_JISEUC-JP などWHATWG Encoding Standard のラベルはすべて対応しています。
Q対応エンコーディングの一覧を取得できますか?
AJavaScriptの標準APIに一覧を返すメソッドはありません。必要なラベルを配列にして、上記の isEncodingSupported() で1つずつ判定する形になります。対応ラベルの定義は WHATWG Encoding Standard を参照してください。
QShift_JIS でエンコード(書き出し)できますか?
A標準の TextEncoderUTF-8専用で、new TextEncoder("shift_jis") としても引数は無視されUTF-8になります。Shift_JIS で書き出すには外部ライブラリ(encoding.js など)が必要です。なお読み込み(デコード)TextDecoder("shift_jis") で可能です。
Qラベルの大文字小文字や別名は区別されますか?
A区別されません。"UTF-8""utf-8" も、"Shift_JIS""sjis" も同じものとして扱われます。正規化後の名前は new TextDecoder(label).encoding で確認できます。

まとめ

JavaScriptでエンコーディングのサポート有無を判定する方法のポイントを整理します。

  • 基本は new TextDecoder(label)try/catchRangeErrorで未対応)
  • UTF-8Shift_JISEUC-JP もすべて対応(true)
  • ラベルは大文字小文字・別名を区別しない。.encoding で正規化名を確認
  • TextEncoderUTF-8専用(エンコードは多言語非対応)
  • バイト列の妥当性は { fatal: true } で検出できる

関連として、日本語をBase64形式にエンコード・デコードする方法文字コードを取得・変換する方法文字コードを判別する方法もあわせて確認すると、文字コードの扱いに強くなれます。