文字コード判定

文字コードの判別

[23] 文字列であるはずのバイト列からその文字コード (文字符号化) を決定するには、 決め打ち (例: UTF-8 固定)、 メタ情報 (例: charset 引数) 利用、 バイト列自体からの推定など、 いろいろな手法があります。

[24] 推定手法やそれらの組合せは不確実性を伴うものの、現実には非常に広範囲かつ頻繁に用いられています。

文字コードの決定

[92] 文字コードの決定は、 バイト列とそれに関係する一連の情報から、そのバイト列の解釈に使う文字符号化を決定する操作です。

[93] ファイル形式転送プロトコルプラットフォーム、 各種文字コード体系、その他慣習や互換性等が絡んだ複雑な問題です。

[94] それぞれによっていろいろな規定や実装戦略がありますが、次のように一般化できます。

[95] 文字コードの決定
  1. [96] 決定的指定
  2. [97] BOM
  3. [98] 上書き指定
  4. [99] 転送プロトコルによる指定
  5. [100] ファイル形式依存の指定の検知
  6. [101] 環境符号化の継承
  7. [102] バイト列等からの推定
  8. [103] プラットフォーム設定に基づく既定値
  9. [104] 最終既定値

[114] 通常は符号化を1つ決定することがこの手順群の目的ですが、 文字コード指定メニューの推奨候補の選出のように、 いくつも符号化の候補を抽出するのが良い場面もあります。

インターフェイス

[391] 文字コードの決定の処理の入出力は、どのような場面で使われるかによっても変わってきます。

[392] 入力となり得るもの:

[394] 出力となり得るもの:

[427] 判定器の入出力の設計は、 Web の場合を例にすると、 encoding sniffing algorithm の他、 復号器, 符号化の変更, MIME sniffing, speculative HTML parser, HTMLの字句化, fetch (ネットワーク, キャッシュその他) といった処理を、無駄なデータ複製を抑制しつつ、 適度にプロセススレッドを分離しながら、 いかに全体として統合していくか、 とういう総合設計の問題の一部分となります。

入力バイトストリーム

[410] 符号化が外部的情報だけでなくデータの内容の検査からも決める場合、 当然のことながら判定器にデータを構成するバイト列を与える必要があります。

[411] バイト列をどのように与えるのが良いかはプラットフォーム応用により様々で、 一概には言えません。


[414] ファイル名の文字コードの処理のような小さなデータしか扱わないと決めている判定器なら、 プログラミング言語の基本的なバイト列型で十分かもしれません。

[412] HTMLCSV のように巨大かもしれないし小さいかもしれないファイルは、 バイトストリームとして引き渡し、判定器が自由に読めるようにするのが良いと考えられます。

[413] 自由にとはいいつつ、一般的な実装では先頭から順に好きなところまで読んでいく形になります。 といっても判定器を構成する小判定器群がそれぞれ順に走査していくような UnivCharDet 型のアーキテクチャーを採用するなら、 一度しか先頭から走査できないようなデータ型は不都合かもしれません。

[415] HTTP応答などネットワークで転送されてきた巨大かもしれない、 もしかすると無限の長さを持つかも知れないストリームを対象とするときは、 それを対処可能なデータの渡し方を採用する必要があります。

[416] よくあるのは、バイト塊を渡すメソッドと、 EOF を渡すメソッドの2つを用意し、新規データが到着するたびに前者を呼び出す手法でしょうか。

[417] 判定器は、分割してバイト塊を受領するときは、 前の塊を処理した最終状態を次の塊の最初で復元できるように設計する必要があります。

[418] 例えば2バイト文字の最初のバイトが塊の最後にあったときは、 そのまま未確定の状態を保持しておき、 次の塊の最初を第2バイトとして処理を再開することになります。

[419] 巨大ファイルの先頭部分だけを判定に使う場合のように不完全な入力を判定器に渡すことを認める場合には、 データの末尾 (EOF) と末尾ではない最後を区別する手段が必要です。

[420] 例えば2バイト文字の最初のバイトと解釈出来るものが塊の最後にあったとき、 その続きが EOF なら不正 (2バイト符号ではない、または壊れたデータ) と判断できますが、 EOF ではなく判定のための入力データの末尾なら、 不正かそうでないかは判断できません。

[421] アーキテクチャーによっては、判定器から応用に対してそれ以上追加の入力データの供給が不要であることを通知する手段があってもいいかもしれません。

[422] 例えばバイトの塊を供給されながら動作する判定器は、 入力に BOM があって UTF-8 と確定したら、 それ以上の入力は不要であると通知すると無駄な入力を抑制できます。

[426] HTMLencoding sniffing algorithm の如く、 判定に使う入力の長さや待機時間長に制限がある場合もあります。 資源ヘッダー こうした入力の制限は、 判定器の側と判定器にバイト列を供給する側のどちらで実装するか、 ネットワークの実装等を含めた全体構成の実装戦略 (>>427) 次第となることでしょう。


[455] 書庫ファイルファイル名のように、 1つだけの入力バイト列で十分信頼できる結果を導出できるか不安があるときに、 複数のファイル名をまとめることで幾分精度を向上できる (かもしれない) 場合があります。

[456] ZIPファイルのように UTF-8ファイル名文字コード未詳のファイル名を混在させられる書庫形式もあります。 共通のディレクトリー名部分のように、 UTF-8ファイル名を 「正解データ」 として参考にできることがあります。

結果として得られる符号化

[428] 判定器の出力は文字符号化の種別です。

[429] 理想的には正解が1つ即座に返されてほしいですが、必ずしもそれが可能とは限りません。


[430] 判定器の諸実装は、どれとも決めかねるときは、

... のどちらかのことが多いようです。他に

... ものもあるようです。

[437] 判定器の選定時に注意したいポイントです。

[433] 正常動作の2タイプのどちらがいいかは使い方によるので一概には言えませんが、 判定器単体の実装としては結果無しを応用に伝える方が便利かもしれません。 判定の処理の全体としては、テキストファイルとして処理するのであれば、 何らかの回答を決める必要があります。

[434] HTMLencoding sniffing algorithm は、 どうしても決められないときは利用者ロケールから最終回答を決めます。

[435] テキストファイルとしての処理を断念してバイナリーファイルの処理に移る応用もあるでしょうから、 その場合は結果無しを最終回答するのでも良いでしょう。


[438] 候補が複数あるときでも、多くの判定器の実装は最善と思われる1つを返すようです。

[441] 複数の候補を返す、または返せる判定器の実装もあるようです。

[439] HTML の表示やテキストエディターの読み込みなど、 大概の処理はどれか1つの結果を選んでその先の処理に進むことになるので、 1つに絞り切る決定的な証拠がなくても、どれかを選ぶしかありません。

[440] 文字コード指定メニューの優先候補表示など、 判定結果を複数提示可能な場面もありますから、 複数の候補を返せるなら、それも悪くないかも知れません。

判定された文字コードの識別

[442] 文字符号化の種別は、文字コードの識別の手法によって伝達することになります。

[443] 文字コードの識別は非常に混乱した分野であり、 同じ名前でも違った文字符号化と解釈されたり、 そもそもその名前が実装されていなかったりといった非互換性に溢れています。 文字コードの識別

[444] 文字コードの識別を確実に行うことは相互運用性セキュリティーのためにどの文脈でも重要には違いないのですが、 文字コードの判定の処理では特別に重要です。 判定器が返した結果はほとんどの場合に復号器の入力になりますから、 判定器と復号器文字コードの識別の共通理解を有している必要があります。

[446] 判定器の諸実装は、 IANA charset を採用するもの、 プログラミング言語の標準ライブラリーの名前を採用するもの、 iconv の名前を採用するものなど方針がバラバラです。 開発過程で二転三転して混乱している実装もあるようです。

[445] どの名前がどの文字コード体系を表すのか標準化され安定しているのは Encoding Standard符号化名符号化ラベルがほぼ唯一であり、これを中核とすることが望ましいと思われます。 ただし Encoding Standard は欧米の Web 系の符号化に偏向しており、 Web 以外やアジア系の符号化に弱いのがネックです。

未対応の符号

[457] 文字コードの判定の処理が、復号器が対応できない文字符号化を回答とすることがあります。 大別して2種類あります。

[460] >>458 は、例えば HTTPcharset="" 引数に未知の文字符号化が指定されたときです。

[461] こうした場合、未知の符号であるとして処理の全体を中断するのが良いと考えられる場合もあれば、 他の符号としての解釈を試みる場合もあります。

[462] HTMLencoding sniffing algorithm は、 諸手法のうち前段の手法で未知の符号化が得られたときは、 採用せず後段の手法を試みるよう定めています。

[463] >>460 のように符号化ラベルが未対応なだけで符号自体には対応しているときは、 他の手法で救済できる可能性があります。

[464] また、共通部分が多い似た符号が他にあれば、 それを使えば不完全でも復号できる可能性があります。

[466] 応用側から予め対応可能な符号の一覧を判定器に渡して、 それ以外は出力させないという設計もあり得ます。

[465] 相互運用性セキュリティーにも関係する問題なので、 関係する仕様との整合性やシステム全体の実装戦略を勘案しつつ決めるべき事項です。

[467] どう設計するにせよ、判定器は未知の符号化ラベルに遭遇しても異常動作をするべきではありませんし、 応用は判定器が未知の結果を返しても異常動作をするべきではありません。

[248] なお >>384 も参照。

結果の確信度

[447] HTMLencoding sniffing algorithm は結果が確定か暫定かの別を持ちます。 HTML では確定的な符号化の情報が見つからないときの暫定結果を後からより確定的な情報が得られたときに変更できます。

[448] HTML 以外でも、文字コードの決定の根拠が HTTPヘッダー等の信頼できる情報なのか、 推定なのかの違いがセキュリティーその他に関係する場合もあることでしょう。


[449] 判定器は出力した結果やその他の候補の符号化それぞれの得点を内部的に持っていることがあります。 UnivCharDet の系譜の諸実装は確度 (confidence) [ 0, 1 ] を計算し、最大のものを結果としています。他の実装にも似たような尺度があります。

[450] 判定器の実装の中にはこうした値を出力できるものもあります。

[451] ただ、こうした値は特定の実装でのみ意味を持つものです。 同じ実装でも版によって判定結果が変わることがあります。 こうした値を得ても、当該判定器の開発者以外にとっては大した情報にはなりません。

[452] とはいえ複数の符号化の候補があるときは、相対的な大小で候補の順位を決めることができます。

[453] また、非常に強い可能性なのか、同じくらいの可能性のいくつかの候補なのか、 消去法なのか、といった違いを利用者インターフェイスでうまく表現できる応用もあるかもしれません。

[454] そのためには、 ただの内部的な値の出力ではなく、 可能な値の範囲とその意味を厳密に定めた判定器と応用との間の API としての値空間が必要です。 現時点でそのような事例は見当たりません。

結果を引き渡すタイミング

[468] 短い入力しか扱わない判定器なら、関数のような形で単純に入力に対して出力を返す形で結果を応用に引き渡す仕組みでも十分です。

[469] ネットワークからのデータなど入力サイズが短かったり長かったりし、 時には無限のこともあるなら、いつどのように結果を引き渡すかを考える必要があります。

[470] 例えば HTML の場合は入力の最初の1024バイトに <meta charset> があればその結果を返せますが、それがなければ最後まで読む必要があるかもしれません。 極端な場合、長く ASCII文字が続いた後に少しだけ非ASCII文字が出現することもあり得ます。 HTML には符号化の変更の仕組みがあるので、 最後まで読み終わらなくても復号レンダリングの開始を実行して、 後から再読み込みするという手法を採れます。

[471] そうした分野で使う判定器なら、判定器が良いと判断したタイミングまたは応用が欲したタイミングで暫定結果を応用側に引き渡し、 引き続き処理を続け、 末端まで到達したら最終結果を応用側に引き渡す、 といった何度も結果を作って渡せる仕組みが必要になります。

上位層文字符号化の推定

ファイル形式の判定

[106] 当該バイト列がどのような性格で、どのようなファイル形式データ形式なのかがわかれば、 文字コードの決定の処理が限定されることがあります。

[107] 当該ファイル形式等に決定方法の規定があれば、それに従うことになります。

[25] そうでなくても内容がある程度限定される場合は、それを前提とした検出手法を採用できます。

[472] WebVTT なら UTF-8 と確定できます。

[473] ZIPファイルファイル名なら、歴史的に ZIPファイルで利用された事例がある文字コード体系のいずれかに絞り込めます。

[474] HTML なら、 HTML Standardencoding sniffing algorithm として決定手順が定められています。


[108] 場合によってはファイル形式の検出と文字コードの決定が同時に処理されることがあります。 sniffing

[390] バイナリー判定関連: >>346

[109] エディターテキストファイルを開く場合など、 特定のファイル形式であるとは判明していないものの、 特定のファイル形式の特徴をも文字コードの判定に活用できる場合があります。


[105] Web の場合については encoding sniffing algorithm を参照。

[56] それ以外のファイル形式依存の方法については charset sniffing も参照。

利用者や応用からの明示的な指定

[110] 利用者文字符号化を明示的に指定する手段が提供されることがあります。 文字コード指定メニュー

[111] 通常はこれが最優先されるべきですが、セキュリティー等の理由で好ましくないとされる場合もあります。


[112] CLIコマンドラインオプションAPI引数などプログラムの実行者が明示的に指定する手段が提供されることがあります。

[113] こうした方法の指定が最優先されるべきか、他の指定を優先するべきかは、時と場合によります。 XHRoverride charsetBOM よりは優先されないなど、 他の指定が優先されることもあります。

[116] ファイル形式によって確定的な符号化を1つ選べることがあります。 例えばファイル形式WebVTT と確定しているなら、 文字コードUTF-8 と断定できます。 そのような場合ですらも、 エディターテキストファイルとして開く場合のように、 ファイル形式に基づく確定的な決定は利用者の指定で上書きできることが望ましい場合があります。

転送プロトコルによる指定

[118] HTTPヘッダーMIMEヘッダーContent-Type: に指定された MIME型文字コードを表す charset 引数を伴っている場合、 これが転送プロトコルによる指定に当たります。

[119] その指定方法や解釈方法にはMIME型ごとに少しずつ違いがあるので注意も必要です。 charset

[120] Web では MIME型による規定の違いは必ずしも尊重されず、ほぼ一律に (MIME charset ではなく) Encoding Standard符号化ラベルに読み替えられて解釈されています。 encoding sniffing algorithm, x-user-defined

[121] MIMEHTTPcharset既定値US-ASCIIISO-8859-1 とする規定を持っていましたが、 実情とまったく一致しておらず完全に無視されてきた歴史を持ちます。 charset charset の不存在を HTTPMIME文字コードの暗黙的指定とみなすべきではありません。

[122] HTTPサーバーISO-8859-1UTF-8 やその他各地域の一般的な文字コードを機械的に charset として指定することがあります。 こうした機械的な指定は実態と乖離していることがしばしばあります。 Webブラウザーによる文字コード判定の失敗事例集

[123] 機械的な指定と著者による意図的な指定を区別するのは困難であり、 原則的には盲信することとなりますから、 文字コード指定メニューなどそれを手動で上書きできる機能が必須となります。


[197] かつては Webブラウザーフォームの提出query parameterフォームデータ_charset_符号化ラベルが設定されてサーバー側に送出される仕組みがありました。

[232] また、これに類似するものとして、一部の Webアプリケーションie のような名前の引数符号化ラベルを設定していました。

[233] Webアプリケーションサーバー側の処理 (CGIスクリプト等) はこうした入力における指定をあれば、それを採用して引数復号を行っていました。

データ内部の指定

[559] 例えば: <meta charset="">, encoding宣言, -*- coding -*- charset sniffing

フォント指定からの推定

指定の読み替え

[206] 文字コードの指定には色々な表現法があります。また、それぞれに複雑な事情が色々あります。 指定された文字コードの名前等はそのまま使うのではなく、適宜の読み替えが必要になります。

[207] HTMLprescan では、 ASCII 系の文字コードであるにも関わらず UTF-16 系の文字コードが指定されたとき、これを UTF-8 に読み替えることになっています。 prescan, UTF-16, 符号化ラベル

[208] x-user-defined は歴史的理由により Windows-1252 に読み替えられることがあります。 x-user-defined, prescan

[115] キリル文字の文字コードは、歴史的理由により、蒙古語を表すことが確実な場合 (例えば .mn ドメインから取得した場合) にロシア系の規格ではなく蒙古の規格に読み替える必要があります (>>187)。

[209] Web では同じ符号化にいろいろな符号化ラベルがあります。 本来は異なる文字コードを指していた符号化ラベルが、 歴史的理由によって統合されている場合が多々あります。

[210] 同じ文字コード名でもインターネットメールWeb とで異なる歴史的経過を辿っており、異なる読み替えが必要となる場合もあります。

環境からの継承

[124] フレームとしての埋め込みHTML から CSSJavaScript の参照のように、「外側」からの指定が「内側」で使えることがあります。 環境符号化

ファイル形式依存の方法による検知

[173] HTML では <meta charset> が、 XML では <?xml encoding="" が、 CSS では @charset文字コードの指定の構文です。各仕様はこれを検出する方法を定めています。 encoding sniffing algorithm 他のファイル形式のいくつかにも似たような構文があります。 文字コードの指定, テキストファイルの先頭

[174] また、テキストエディター文字コードの指定の構文を決めていることがあります。 いくつかのプログラミング言語等もこれを採用しています。 -*- coding -*-, vim:, 局所変数群リスト, テキストファイルの先頭

[175] WebVTTWEBVTT など、ファイル形式が確定できる文字列テキストファイルの先頭に検知できれば、 文字コード自体が明記されていなくても自動的にそのファイル形式の規定する文字コードと推定できることがあります。

[244] cmsd_doc_reference.pdf, , https://cms.al-design.jp/downloads/EUC-JP/cmsd_doc_reference.pdf#page=69

<?php require( "cmsdesigner/include/view.php.inc" ); // encoding="euc-jp" ?>

は、定型文として入れてください。(「// encoding="euc-jp"」は Dreamweaver の文字化け(不具 合?)回避の為のおまじないです。)

[245] encoding=""sniffing を応用した hack か?

バイト列等からの推定

[125] バイト列に含まれるバイトを想定される文字コード符号構造と比較したり、 自然言語文字の出現頻度の統計データと比較したりして、 使われている文字コードを推定する手法群があります。

[126] 仕組み上、文字コードを断定することは不可能ですが、 実用上かなり多くの場合に正確な判断を下すことが出来ます。

[127] ローカルファイルや古い Webサイトなど、これ以外に信頼できる方法がないことも多いです。

[128] HTML では頻度解析等の手法と呼ばれ、大まかな枠組みのみとはいえ規定があります。 頻度解析等の手法

[130] ASCII文字のみで構成される場合、復号のみを考慮するなら ASCII でも ISO-8859-1 でも Windows-1252 でも UTF-8 でも EUC-JP でもどの回答でも正解になりますが、 その後の処理を考慮すると判定不能と判断することが望ましい場合があります。 頻度解析等の手法

[134] フォント依存符号化を使った HTML文書では、 <font face> を判定の補助情報に使う必要があります。 頻度解析等の手法

[131] バイナリーデータを与えた場合にバイナリーと判定する判定器もあります。 この挙動が望ましいかどうかは時と場合によります。 既にバイナリーデータを除外したテキストファイルのみが入力のときは、 無理にでもどれかの文字符号化と推定するか、判定不能と返す方がいいことも多いです。

判定器を意識した著者による記述

[135] 文字コードの判定を助けるため、紛らわしい他の文字コードに出現しない文字を含めたり、 当該文字コードで典型的な文字を最初の方に含めたりする技法が使われることがあります。

[137] 文字コードが乱立しながら頻度解析等の手法が未発達だった平成時代初期の Web でよく用いられました。日本など乱立が著しかった地域に多く見られます。

[117] 日本では Shift_JISEUC-JP の区別のため、 0xFDFE, 美乳, などが使われました。 文字コード判定器を意識した著者による記述

[136] 日本平成時代前半の Webアプリケーション (CGIスクリプトなど) は入力データの文字コードの判定が必須の処理でした。 正しい文字コードと確実に認識されるよう、 <input type=hidden> で固定の文字列を用意しておき、 判定の材料とする手法がしばしば使われました。

判定器が必要な場面

[26] 文字コードの判定応用

[240] 1551276 - (chardetng) Autodetect legacy encoding on unlabeled pages, https://bugzilla.mozilla.org/show_bug.cgi?id=1551276

[241] Security: ASCII can be autodetected as ISO-2022-JP [40089450] - Chromium, https://issues.chromium.org/issues/40089450

[242] >>241 Firefox の開発者が ISO-2022-JP を自動判定するのはセキュリティー上の問題だと主張し、 Chrome に判定から除外させた回。ところが Firefox は今でも ISO-2022-JP と判定している。

決定に使う入力バイト列の長さと範囲

資源ヘッダー, sniffing, encoding sniffing algorithm

出所とロケール情報による推測

[138] 判定したいバイト列の出所 (例えば取得に使った URLTLD) や関係するロケール系の情報が文字コードの決定に使われることがあります。

[86] 利用し得る情報の例:

[141] URLファイル名ドメイン名から利用できる情報の例:

[169] 利用方法:


[236] 845791 - Gather telemetry about the necessity of the Russian and Ukrainian encoding detectors, https://bugzilla.mozilla.org/show_bug.cgi?id=845791

[239] >>236 Firefoxキリル文字の文字コードの判定を廃止する非互換変更を企て失敗した回

TLD の利用

[204] 頻度解析等の手法バイト列だけでは似た構造の文字コードの判定に失敗することが少なくないので、 他の情報を補助的に使うことが試みられています。 TLD は特に有力な情報源と考えられています。

[205] ccTLD は、一部の国際的に商業化されたものを除けば、 ほぼ当該地域で使われています。従って当該地域の一般的な文字コードが使われている可能性が、 他の地域文字コードよりずっと高いと考えられます。

TLDによる文字コード判定の補助

[215] ただ、この情報は飽くまでも補助に過ぎません。

  • [216] IPアドレスによるアクセスでは TLD を使えません。
    • [217] 逆引きIPアドレスの割当国データベースに基づく推定も可能ではありますが、 実行コストの高い演算なので、判定ヒントを得るためにしては費用対効果に見合うか疑問です。
  • [218] gTLD では ccTLD による推定を使えません。
    • [219] 昔も今も ccTLD 以外の TLD は全世界的によく使われています。
  • [220] ccTLD でも国外で多く使われている事例がいくつもあります。
    • [222] 例えば .tv は国外のテレビ業界で使われがちです。
    • [221] ccTLD を使う手法はこうした用途が多い ccTLD を除外しています。 TLDによる文字コード判定の補助
      • [223] しかし完璧ではなく、国外利用が多くても除外されていないことがあります。
      • [224] 国内利用についても ccTLD に基づくヒントを供給できなくなる弊害があります。
  • [225] 正書法改革や表記法の対立、内戦などを抱えている国では、 ccTLD のヒントが機能しにくいことがあります。

[226] TLD は若干の傾斜を与えたり、最終的に判断がつかなかったときの既定値を決めたりするのに使うのがいいのでしょう。 TLD に基づき候補を絞り込んでそれ以外を除外したりするのは避けておくのが無難です。

[227] 逆に言うなら、

両方が必須です。

[238] >>237 FirefoxgTLD での文字コードの判定を廃止する非互換変更を企て失敗した回


[187] HTTP charsetHTML <meta charset> などで指定された文字符号化は、 .mn の場合、次のように置換するべきです。 ロケール等による文字コード判定の補助

ロケールの利用

[87] プラットフォームロケール言語の情報が文字コードの判定のヒントに使われることがあります。

[195] ced利用者インターフェイス言語情報があればヒントとして使うことがあります。 >>43

[38] subtitle文字コードの判定言語情報が使われることがあります。 ロケール等による文字コード判定の補助

[39] ZIPファイル文字コードの判定の補助または既定値の決定に POSIXロケールが使われることがあります。 ZIPファイルの文字コード


[178] HTMLテキストファイルnavigate では、 他の方法で決められないときの既定値ロケール依存となっています。 >>177

[179] より正確に言えば、実装定義または利用者指定の既定の文字符号化とすると定められています。 >>177 現実的には利用者の言語から決めることになります。

[180] 制御された環境や文書の符号化を予め決められる環境では、 UTF-8既定値とするのがよい (suggested) とされます。 例えば新しいネットワークの専用の利用者エージェントではそうできると述べられています。 >>177

[181] 具体的にそのような事例があるのかは不明です。 仕様書としては可能性を狭めないために「新しいネットワーク」 のようなものを想定しているのでしょうが、 現実的にそうしたものが大々的に導入される機会があるかは不透明です。 (例えば HTTPSHTTP/2 への移行でも、サーバー内容は従来のままなので、 切り替えの機会とはできなかったわけで。) 特定のイントラネットや新しい種類の端末の専用ネットワークでも、 わざわざ既定値を変えるための設定や実装の変更よりは HTTP charset の指定を徹底させる方向性の方が楽そうで。

[182] それ以外の環境に対しては、 利用者ロケール利用者がよく見るWebページ自然言語符号化と相関があると考えられるため、 ロケール典型的には依存 (typically dependent) して既定値が定まるとされます。 >>177 利用者の言語, ロケール依存の既定の文字コード

[194] UTF-8頻度解析等の手法で高い確率で判定可能です。 であるなら UTF-8 を既定値にするよりも、 既定値は Web 初期の文字コードの指定の慣習が無かった時代の Webサイトをより良く救済できる可能性が高い値を選ぶのが良いと考えられます。

[235] Encoding detector causing compat issues [41301730] - Chromium, https://issues.chromium.org/issues/41301730

I'm not sure when exactly Chromium diverged from WebKit, but the status prior to M55 (for several years) is that Chromium, by default, did no sniffing whatsoever and just used a system locale default. Secondly, if the user ever clicked "Autodetect" in the encoding menu, this acted as a permanent setting, and in that case ICU autodetector would run on 100% of page loads, overriding all headers, and supporting the entire set of ICU encodings.

Starting at M55, we removed all menus and all influence of system locale, and started to run CED autodetector by default but only affecting pages without headers.

To my knowledge, Chromium has never shipped a Japanese-specific sniffing configuration.

その他の試み

[69] >>68 Accept-Language:, User-Agent: (OS), アクセス者の IPアドレスを使って文字符号化を推定する。 平成25年。

符号構造や出現頻度などによる総合的な推測

[2] 文字コードの指定の構文が存在しない (または利用されていない) 文字列バイト列があるとき、 その文字コードバイト列それ自体から推定することになります。

[377] この推定は、どうしても不確実性が伴います。様々な手法が開発され、 それらを組み合わせることで精度が高められてはいますが、 いずれの手法も絶対的とは言えません。

[378] この種の技法は3通りに大別できます。

この3種は必ずしもきっちりと分類できるものではなく、 境界的技法もありますし、実装上は組合せて実現されることもあります。

[382] 例えば符号構造上あり得ないバイトは条件分岐のような形で単独で判定することもできますし、 統計的手法による期待出現頻度 0 と記述して計算することもできます。 どのように実現するかは実装戦略上の判断になります。

[383] 各技法の詳細はこの後の各節を参照。


[230] 平成時代初期 (西暦1990年代) には Web 等で文字コード情報のないテキストファイル言語の境を超えて多く流通するようになり、 文字コードの判定の手法の研究と実装が各所で行われました。

[5] 特に日本キリル文字圏では、 複数の文字コードが同程度に広く使われていたために自動判定が重宝されていました。

[331] 21世紀に入ると単一の実装で全世界に対応する多言語対応手法の普及が進みましたが、 文字コードの判定も単一地域の利用者が遭遇する文字コード体系に限らず、 全世界の文字コード体系を対象とする必要が生じ、複雑化しました。

[332] 逆に Web では charset 引数等による明示的な指定が普及し、 追って UTF-8 への統合が進んだことで、 バイト列からの推定による文字コードの判定の出番は減少しました。

[333] とはいえ、過去のデータやイントラネットZIPファイルCSVsubtitle など旧来の文字コードが混在する領域も依然として残っており、 文字コードの判定の技術の需要が失われたわけではありません。

対象とする符号

[384] 判定器は、対応する符号 (文字コード体系) を決める必要があります。

[385] 多くの符号に対応している判定器は便利ですが、 どんな判定手法も完璧でない以上、 誤判定とのトレードオフになることには注意が必要です。 対応符号数が多いほど誤判定のリスクが高まります。 似た構造の符号が含まれると判定問題の難易度が急激に上がります。

[386] 判定器の実装戦略にもよりますが、一般に符号数が増えれば増えるほど、 判定速度は劣化し消費メモリー量は増大します。 判定に必要な計算や参照するデータが多くなるためです。

[387] 従って判定器の実装者は適用対象の分野をよく分析し、 必要な符号を過不足なく選ぶ必要があります。 Webにおける文字コード, ファイル名の文字コード

[423] 便利な工具として使える独立した判定器なら、多少処理に時間がかかっても、 あらゆる候補を徹底的に分析して回答を出すことに意味があるかもしれません。

[424] 一方で Webブラウザーに組み込まれた判定器なら、 文字コードの判定は数多くの処理の一部に過ぎませんから、 時間をかけるより高速に動作することが、 多くの検証用データを保持するより小さな実装であることが求められます。

[425] ZIPファイルファイル名の文字コードのための判定器は、 およそ出現するとは考えにくい UTF-16ISO-2022-JP のような符号を最初から候補から除外しておく方が何かと良さそうです。

[388] セキュリティーにも注意が必要です。 例えば、本来ただの ASCII文字列と判断されるべきものに UTF-7 を注入し、 UTF-7 と判定させることで任意プログラムを実行させるような攻撃手法があり、 UTF-7 という符号化方式自体が危険と認識され忌避されるようになった事例があります。 文字コードの判定という演算が行われること自体が相応しくない状況もあれば、 文字コードの判定で特定の符号を認識することに問題がある状況もあり、 符号化方式の側に汚名が着せられるケースもあり、 ケースバイケースの判断が必要です。 文字のセキュリティー, Webセキュリティー

[389] 特定の符号と判断された場合に機能制限モードに切り替えるとか、 外部的指定が無く文字コードの判定が必要とされたときに機能制限モードに切り替えるとか、 対策の仕方もケースバイケースです。

符号構造に基づく判別

[231] 文字コードの判定の基礎的な技法の1つが符号構造を利用するものです。

[58] 平成時代中頃までの古典的な方法では、 文字符号化によって符号の構造が異なることを利用し、 ある文字コード体系で出現する符号かそうでないかという構造的知識を主に使っていました。 しかしこの方法単独では符号構造が重複する領域で互いの区別が付きづらく、 あまり精度が上げられませんでした。 ただ、実装が容易ではあるので、幅広く用いられましたし、現在でも使われることは珍しくありません。

[59] 例えばシフトJIS日本語EUCは第1バイトに使われるバイト、 第2バイトに使われるバイトの範囲がそれぞれ違っていますので、 その範囲に収まるかによってどちらか判断できることが多いです。 しかし完全に重なる部分もあるため、そのような符号ばかりだと正しく判定できません。

[60] また、半角カタカナを利用すると両者の重なる領域が著しく増えるため、 誤判定が多くなり、頻繁に半角カタカナ文字化けを目にすることになります。 これは半角カタカナが嫌われる大きな要因の1つにもなっていました。


[334] 多バイト符号の判定では符号構造を理解し区別することが基礎となります。

[336] ただし各符号の構造は共通の部分もありますから、完璧ではありません。

[339] 特に EUC-JP, gb18030 (= EUC-CN), EUC-KR (= UHC) はいずれも EUC を採用しているので、基本的な文字が収容される CS1 が同一の構造となっています。

[337] また、 EUC-JP の2バイト文字の少なくない数が、 Shift_JIS の1バイト文字 (半角カナ) 2つ分に当たります。

[338] こうした同一部分は判定に使えません。 同一部分を除いた残りの固有部分で、どの符号かがわかります。 ところが基本的な文字が同一構造の部分にあると、 固有部分が文字列に含まれているとは限らないので、 判定できないことになります。

[340] 狭義の構造だけで判断できなくても、 空き領域で判断できることがあります。 2バイト符号には使われていない領域も多いですが、 各符号で少しだけ空きの範囲が違います。 空きが含まれていれば、その符号は候補から除外できます。

[341] ただし、空き領域が外字として利用されてきた歴史があることには注意が必要です。 空き領域が出現したからといってただちに除外すると、正解も除外されてしまうおそれがあります。


[335] 1バイトの7ビット符号8ビット符号の判定では、 符号平面のほとんどのビット組合せに有効な文字が割り当てられてしまっているため、 符号構造とその利用からの判断の余地はそれほど大きくありません。

[342] それでも一部の符号には空き領域があるので、それが出現すれば、 その符号は候補から除外できます。


[343] 1バイトの8ビット符号CR多バイト符号の1バイトの未使用のビット組合せは、 基本的には使用されていません。極稀に外字等で利用されることがあるものの、 Web 等で流通するデータに利用されることはまずないと考えられます。 これらは多バイト符号の先頭バイトに使われる領域なので、 当該多バイト符号以外を除外する条件として使えます。

[344] 0x7F も条件は近いのですが、ほとんどの符号で使われておらず、 多バイト符号の1バイト目にも2バイト目にもまず出現しないので、 どう扱ってもかわりありません。


[345] CL のうち、空白等 ([ 0x09, 0x0D ], 0x1A) と ISO/IEC 2022 で使う制御機能 (0x1B, 0x0E, 0x0F) を除いた部分も、通常の文字列に出現することはまずありません。

[347] これらも、 ASCII の系譜の文字コードの判定にはほとんど関与しません。

[348] ただし、 VISCII 等では図形文字を割り当てていること、 OEMコードページでは図形文字を呼び出すために使われたこと、 には注意が必要です。

[346] 文字コードの判定の実装の中にはバイナリーという判定結果を返すことができるものもあります。 この領域が出現し、 VISCII, UTF-16 等でないなら、バイナリーと判断できます。

[349] 文字コードの判定の実装がバイナリーをどう扱うかは悩ましい問題です。 用途と実装戦略次第のところではありますが、明らかに文字列ではないバイト列の処理を延々と続けても仕方がないと考えると、 バイナリー、あるいは「結果無し/不明」という回答を用意して、 それが確定的になれば早々に処理を打ち切るという選択肢もあることでしょう。


[78] 対応している符号化を順番に試してエラーにならなかったものを採用するというだけの実装を 「文字コードの判定」だと称しているものがたまにあります。 このような方法は符号構造がまったく違う符号の区別になら使えますが、 多くの8ビット符号の区別が不可能です。

[257] このような原始的な手法では Shift_JISEUC-JP の区別すら完全にはできないことが日本では古くからよく知られています。

[79] 符号構造が限定される場合なら、その限定される特徴で判定したほうが高速かつ確実なことが多いので、 この手法が役に立つことはほぼないといっていいでしょう。

UTF-8

[129] UTF-8 はかなり確実に判定できることが知られています。 頻度解析等の手法

[288] UTF-8 の2バイト以上のバイト列が含まれ、それ以外に非ASCII文字が含まれないなら、 UTF-8 と判定してほぼ間違いありません。

[285] ただし8ビット符号の領域を使っている以上誤判定の可能性が皆無ではありません。

[287] ced は4種類の2バイト列を UTF-8 ではなく windows-1252 に加重する例外条件を持っています。 >>286

BOM

[1] Web では歴史的事情により BOM の存在がかなり重視されています。 encoding sniffing algorithm

[57] BOM に対応した仕様や実装でも、どの文字符号化BOM を検知するかはかなりブレがあります。 現在の WebUTF-16UTF-8 に限定しています。 過去の WebWeb 以外の実装はそれ以外にもいろいろなものに対応していたり、 いなかったりします。

[132] BOM による検知は常に適用できるものではなく、使わない場合もあります。 例えばファイル全体ではなくプロトコル要素として用いられる文字列片では BOM が認められていない場合が一般的であり、その場合たとえ BOM のように見えたとしてもそれは本来の文字列の先頭です。 文字コードの判定には使えません。

[133] ZIPファイルファイル名文字コードの判定では BOM 検査を行いません。

ISO/IEC 2022 指示シーケンス

[355] ISO/IEC 2022指示シーケンスは自己識別的に符号化文字集合を特定する情報を含んでおり、 文字コードの判定上極めて重要です。

[356] ただし実務上いくつか注意が必要です。

日本語文字コードの識別用符号列

[370] 日本語圏ではシフトJIS日本語EUCの区別のために特徴的な文字列を挿入する手法が開発されてきました (>>135)。

[371] こうした手法は Shift_JISEUC-JP の区別にはそれだけで有益であったとしても、 それ以外の世界の文字コードが候補となるとき、必ずしも十分な根拠にならないこともあります。

[372] しかしせっかく著者が提供してくれているヒントを判定器が無視するのももったいないのであり、 BOM指示シーケンスほどの確実性は無いとしても、 それに準じた重要情報として利用することが期待されます。

gb18030 の特徴的な符号列

[275] gb18030 の4バイト符号は、第1バイトと第3バイトにバイトが使われ、 第2バイトと第3バイトは ASCII数字バイトが使われるというかなり独特の構造をしています。 主要な多バイト符号ASCII数字を第2バイトに使うのは gb18030 だけです。 各種の8ビット符号でこうした並びが出現することもあるでしょうが、 特殊な用例に限られるのではないかと思われます。 そこでこうした並びを数えて、 gb18030 と判定する有力な根拠として使うことができます。

判定条件からの逸脱の扱い

[350] ある符号で使わないバイトの出現でその符号を候補から除外することは、 ときに正解の符号まで過剰に除外してしまうおそれがあります。

[351] 空き領域が出現した候補を除外すると、外字が入ったデータを正しく判定できない場合があります (>>341)。その符号における空き領域の利用実態を勘案して出現頻度の閾値を決めるなど、 曖昧な判断が望ましいことがあります。

[352] 符号構造から逸脱した候補を除外すると、破損データを正しく判定できない場合があります。 古いデータには特に言えることですが、 生成や転送の過程の何らかの問題で多バイト文字の一部が破損したり、 関係ないデータが混入したり、 といった理由で通常の符号構造に沿わないバイトが出現することが、 ままあります。一度の違反で即決せず、 バイト列の全体の傾向から総合的に判断するべき場合があります。

[353] 判定条件を弱めれば弱めるほど不規則データへの耐性は高まりますが、 他の符号と誤認する可能性も高めてしまうことになります。 あらゆる破損に対応することは原理的に不可能で、 どこかで線引きが必要です。

[354] 正常データのほとんどすべてを正しく判定できつつ、 異常データもできればなるべく作成者の想定に近いものと判定できることが好ましいと考えられますが、 具体的にどのような状態を良しとし、 どのようにそれを実現するか、は判定器の適用分野と実装戦略次第になります。

典型的バイト列による推測

[249] 符号応用に依存した「ありがち」なバイト列のパターンが出現することがあります。

[250] どれくらい決定的な根拠にできるかはケースバイケースで、 他の符号との区別や「ありがち」な度合いによって調整が必要となります。

8ビット符号の特徴的な符号列

[265] かなり多くのWebページ©を使った著作権表記を持っています。 ©の前は空白かタグで、 ©の後は空白かタグか、年号著作権者が来ます。

[266] windows-1252 やいくつかの文字コード体系で 0xA9© があります。 >>265 のようなバイト列のパターンを発見できれば、 そうした文字コード体系である可能性が相当高くなります。

[269] 0xA9Shift_JIS半角カナ に当たります。 幸い は直前にカタカナが来ることがほとんどで、 >>265 のようなパターンで出現することはまずありません。 Shift_JIS でないことを示す徴証として使うのが良いと考えられます。

[318] 他の多バイト符号では第1バイトに使われることがありますし、 Shift_JIS でも第2バイトには使われることがあります。 前後が空白, という条件が重要になります。 多くの多バイト符号だと第2バイトに <> が来ることもないので、 タグ空白と同等に扱えます。 それ以外だと徴証としては少し弱くなります。

[317] windows-1252 やいくつかの文字コード体系で 0xAE® があります。 0x99 があります。これらは語末に出現します。

[319] 英数字の後に半角カナ 0xAE が1つだけ出現して空白が来ることはほとんどないので、 Shift_JIS ではない可能性が高いと判断できます。

[320] しかし 0xAE0x99多バイト符号の第2バイトになることがあるので、 非ASCII文字の後に 0xAE0x99 が来るケースでは注意が必要です。

[321] ced は 「NESTLÉ®」 を UTF-8 と誤認される windows-1252 の実例として挙げています。この例のように Shift_JIS 等の他に UTF-8 としても正当なバイト列になり得ることがあるので注意が必要です。

[267] 他に非ASCII文字がなくても©®だけが非ASCII文字として含まれることが、 欧米中央アジアをはじめ、世界的によく見られます。 そうした場合にこれを Windows-1252 と判断することが重要になってきます。

[268] 言語モデルによる判定は letter だけを使いがちで、 © のような記号が除外されていて判定に使われず、文字化けしてしまうことがあります。

[270] chardetng などがこうしたものを windows-1252 と判定する条件を組み込んでいます。 >>289

[290] ただし chardetng はそれでも ©ISO-8859-2 と誤認しがちです。 ISO-8859-2 との区別についてはわざわざ注釈で言及があります >>289 ので、意識して設計されているはずですが、それでも取り扱いが難しいということなのでしょう。

[330] >>329 は 「Copyright ©1997,」 の © 1文字 (0xA9) だけが非ASCII文字です。 ced は正しく判定しますが、 chardetngISO-8859-2 と誤認します。

価格

[322] windows-1252 などいくつかのコードページ0x80 です。通貨記号の後にASCII数字が続くなら、 価格の表記と考えられ、 その文字コード体系であることのヒントとして使えます。

[323] 言語モデルによる判定は letter だけを使いがちで、 通貨記号数字が除外されていて判定に使われず、文字化けしてしまうことがあります。

[324] 0x80 は主要な多バイト符号の第1バイトには使われないので、 空白の後などで重要なヒントとして使えます。 しかし第2バイトに使われることはあるので、注意が必要です。

[325] windows-12520xA4 など他の通貨記号多バイト符号の第1バイトや第2バイトや、 Shift_JIS半角カナに使われることがあるので、注意が必要です。

罫線素片

[326] 多くの OEMコードページ罫線素片等の CUI 描画のための部品文字を多く持っています。 これらは通常の文章には出てこないことが多いですが、図表などで使われることもあります。

[327] こうした文字が1つだけ孤立して出現することはまずないので、 負のヒントとして使うことができます。

[328] ただし縦線はそこだけ見ると前後に別の文字が来る、孤立した文字に見えますから、 単に前後が罫線素片でないというだけでは足りず、少し工夫が必要です。 前後の行との結合を検査すれば確実ですが、 そこまでせずとも、 他に横線 (罫線素片の連続) があるなら同様に罫線素片とみなしてよく、 他に横線がどこにもないなら負のヒントとみなすのがいいかもしれません。

[251] ファイル名に出現することもまずないので、 OEMコードページが使われることが多い ZIPファイルファイル名文字コードの判定では1個の出現だけでも強力な負のヒントになります。

仮名

[510] 日本語文章には仮名が頻出します。

[511] どの文字コード体系でも仮名は連続して特定の範囲に配置されていますから、 仮名が使われた文章は特徴的なバイト列になります。

[512] Shift_JISEUC-JP との区別や、各国の EUC との区別に非常に便利な特徴です。 日本では古くから使われてきました。 近年でも chardetng が利用しています >>252

ハングル

[513] 大韓民国語の日常的な文章は大部分がハングルで表記されます。 漢字は例外的です。

[514] EUC-KRgb18030EUC-JP は基本構造が共通しているので、 一見すると区別がつかなそうですが、いくつか特徴があります。

EUC-JP の特徴的な符号列

[271] EUC-JPCS3JIS X 0212 ですが、 先頭付近のいくつかのダイアクリティカルマーク付きアルファベット等が配置されています。 こうした文字の使われ方を想像すると、 欧州言語単語を表す通常のアルファベットの列の中に孤立して1つだけ混じることが多そうです。 2つ以上続くこともあるでしょうが、1つだけのことが多そうです。

[272] 例えば ASCII文字に囲まれた 0x8F (SS3) と GR のバイト2つで構成される3バイトの列は、 >>271 の文字である可能性が高そうです。 各種の8ビット符号でもこのようなバイト列が出現することは考えられますが、 この特徴的な並びが意味のある言語の語を構成することは余り多くはなさそうです。 そこでこうした並びを数えて、 EUC-JP と判定する有力な根拠として使うことができます。

[273] 実際のところ EUC-JP でこれらの文字を使った (しかし一般の日本語文字はあまり使わない) 文章はそこまで多くないと思われます。 文字コードの判定のためのライブラリーのテストデータに含まれていることもあるのですが、 テスト用に変換して人工的に作った例文と思われます。

[274] よってあまり優先度は高くありませんが、簡単に対応できるならしておいても良いかなという感じでしょうか。

UTF-16 や UTF-32

[373] HTML をはじめとする機械可読データ形式のほとんどは ASCII文字を構文記述に使っています。 Unicode では ASCII文字が [ U+0000, U+007F ] に配置されているので、 UTF-16UTF-32 では 0x00 が規則的かつ大量に出現することになります。

[374] ASCII文字が使われていることが期待される UTF-16 / UTF-32バイト列は、 BOM がなくても 0x00 の数を数えることで高確度で判定できます。

[375] ASCII文字が少ない場合でも、近いブロックに配置された文字が並びがちという特徴を使えば、 UTF-16UTF-32ASCII 系かはそれなりの確度で判定できると思われます。

[376] しかしあまり需要が無いので、研究も実装もそれほどなされていないようです。

統計的手法による推測

[258] 統計的手法は、バイトバイトの連続の出現状況と、 それらが表すであろう文字文字列の出現頻度の統計データとを突き合わせることで符号を推定します。

[259] 有名な換字暗号の解読法に、英語の文章で e が頻用されることを利用して暗号文を解読するものがありますが、 基本的な原理はこれと同じです。

[260] 文字コードの判定問題は、 既知の有限少数個の符号のいずれかに回答候補が絞り込まれていますから、 任意の換字暗号解読問題より簡単です。

[261] しかし符号化されているのが英文とは限らず、 世界中のどの自然言語かわからないし、 混合されているかもしれないし、 自然言語の文章とも限らないという難しさがあります。

[262] ファイル名のように利用できる材料が少ないこともあれば、 ネットワークから取得中のデータのように利用できる材料が徐々に増えていくこともある (= 途中で傾向が変化するかもしれないし、全体像が見えない段階で判断を迫られることもある)、 という難しさもあります。

[478] 統計的手法には原理的な限界があります。 まず、 「対応していない言語は判定できない」という根本的問題があります。 言語の頻度モデルは既知の自然言語に基づいて構築されているため、モデルに含まれていない言語、または収集されていない方言スラングを入力された場合、 推定器は誤った言語モデルを適用するか、雑音として扱ってしまいます。 未知語彙の多いテキスト、記述体系が非標準的な言語は、とくに誤判定が起きやすい領域です。

[477] また、既知の自然言語であっても、通常の文章から逸脱した「奇抜」 な表現は統計的特徴を乱し、精度を大きく低下させます。文学的効果を狙って異常な語彙分布にしたり、 極端に省略・連結した文体を採用したり、比喩的または視覚的な表記 (特殊記号の多用や AAギャル文字などの利用) を行うと、 言語モデルの前提である頻度の安定性が破壊されます。 「ハングル表記の日本語」「ヘブライ文字で書かれた英語」のような、 文字と言語の一般的な組合せと乖離しているデータも、統計的手法の想定外です。

[479] 自然言語的でないデータの扱いはさらに困難で、例えば「文字コード表」「文字一覧表」「索引」 のような資料は、特定の語や文字が異常に均等に、あるいは偏って出現するため、 語の使用頻度に基づく判定はほぼ機能しません。

言語モデル

[263] 多くの判定器は自然言語の文章の文字文字の連続 (n-gram) の出現頻度に基づき言語モデルを用意し、推定に使います。

[264] 具体的にどのような言語モデルを作り、どのように保持し、 どのように処理に使うかは、それぞれの判定器で違います。

[475] 同じ自然言語や同じ文字コードの言語モデルと言っていても、 その実態は実装ごとにまったく違うので、 基本的には他の実装には流用できません。

[476] 例外的に、 UnivCharDet の系譜の各種判定器は共通祖先のオープンソースソフトウェアからの派生なので、 他のソフトウェアのソースコード上のデータや言語モデル生成ツールを流用できることがあります。 その場合でも、言語モデルの使い方に手が入っている実装も多いですし、 判定器内の各構成部分のバランスの違いもありますから、 無調整で流用できるとは限らないことには注意が必要です。

言語対応と言語判定

[480] 設計によるところも大きいですが、 言語モデルは必ずしも1つの自然言語ごとに1つとする必要はなく、 文字の利用度が似た傾向にある複数の自然言語をまとめたモデルを用意することで足りる場合もあります。

[481] 欧州など複数の自然言語で同じ文字コード体系を共有している領域では、 細かく自然言語を区別するよりもまとめた方が精度も処理速度も良くなることもあります。 借用語固有名詞や引用文で混在しがちな言語群は、 細かく分ける方が結果が悪くなるかもしれません。

[482] 言語モデルの種類が多いほど、必要な処理が増えるということですから、処理速度は悪化します。 しかし言語モデルを統合しすぎても、言語の特徴が均されて見えづらくなりますから、 判別精度は悪化します。 実データの傾向を見ながら適度なバランスで分割・統合された言語モデルを用意し、 それらを使って結果を導出する計算のパラメーターを調整していく地道な作業が必要となります。


[483] 判定器のなかには、文字コード判定と併せて言語判定の機能を備えるものも存在します。 確かに自然言語を特定して文字の頻度から符号を判定するという判定器の仕組みは、 自然言語の判定器という側面も持っています。

[484] しかし、この二つの機能を統合することが優れた実装戦略であるかについては慎重な検討が必要です。 文字コード判定と自然言語判定では、要求される言語モデルの精度や粒度が異なるためです。 文字コードの種類よりも自然言語の種類の方がはるかに多く、 細分化された言語モデルが必要になります。また、UTF-8 のように符号構造を利用すれば言語モデルなしで判定可能な符号であっても、 統合してしまうと結局は各自然言語ごとのデータを用意する必要が生じ、 不要な複雑さを抱え込むことになります。

[485] とりわけ欧州の諸言語のように、使用する文字種も符号構造もよく似ている場合、 文字コード × 自然言語の組合せごとに類似したモデルが多数必要となります。 最低でも十数個規模の言語モデルを管理することになります。このような環境では、 計算結果の小さな差異で優劣が入れ替わるので、調整が綱渡りのように不安定なものとなります。

[486] さらに重要なのは、誤判定のコストの違いです。 自然言語の誤判定は大きな影響がないことが多いですが、 文字コードの誤判定はデータの読解不能 (文字化け) という致命的な結果を招きます。 綱渡りの調整で判断を迫られた場合には文字コードの判定を優先せざるを得ませんが、 自然言語の判定精度の足を引っ張るとしたら本末転倒です。

[487] 新たな自然言語への対応を増やす際にも、対応済みの自然言語の判定への悪影響はもちろん、 対応済みの文字コードの判定への悪影響を回避する必要があり、 自然言語の追加の開発コストとリスクが大きくなってしまいます。

言語モデルの開発

[488] 言語モデルの開発は容易ではありません。 対象となる言語の文章を大量に収集し、分析しますが、 世界中の各言語の文章群 (コーパス) の入手からして困難です。

[497] 平成時代初期に開発された UnivCharDet も苦労したと思われ、 言語モデルの都合で西欧中欧への対応に制限があるなど機能性に影響が出ています。 UnivCharDet の系譜のソフトウェアの多くはその言語モデルをそのまま引き継いでいます。

[498] cedGoogle のデータベースを利用しているようです。 Google が世界中から集めた Webページの分析結果を自由ソフトウェアとして利用できるのは素晴らしいことですが、 Google 社外の我々はそれをただ使うことしかできず、 研究することも改良することも、 他に流用することもほとんど不可能という限界も抱えています。

[499] ced に限らず他の判定器の言語モデルも、 基本的には「そこにある」という以上のことはどうにもできない不透明なデータです。 微調整くらいはできるかもしれませんが、 完全に再生成するには同一の元データを用意し、 同じ方法で計算、加工しなければなりません。 完全に再生成できなければ改良もできません。 ところがすべての元データを用意するのは原作者すら不可能な場合が多いです。

[500] これは近年 LLM 等の AI 技術で問題となっている構図とよく似ています。 OSAID

[489] UnivCharDet の系譜のソフトウェアの一部や chardetng >>252Wikipedia の記事を使っています。 Wikipedia には様々な分野の記事が集まっており、 適度に固有名詞外来語も混ざっていると考えられますから、 目的に適った文書群といえます。

[490] ただし Wikipedia が万能ともいえません。 まず、 Wikipedia に存在しないか、十分な記事がない自然言語では適しません。

[491] 幸い、 Unicode 以前に独自の文字コード規格を開発し流通させてきた自然言語の多くは Wikipedia が存在しているようです。しかしフォント依存符号化を使ってきた少数言語などはカバーされていないことがあります。

[492] また、ファイル名の文字コードの判定のような一般の文章とは異なる語彙の偏りを持つ可能性がある対象に適用する場合の Wikipedia 由来のデータの有効性は明らかではありません。

[493] 音楽ファイルのメタデータsubtitle も一般の文章とは異なる偏りを持つ可能性があります。 ただ Wikipedia にも楽曲やアーティストの記事はありますから、 カバーされていないともいえません。 有効なのかどうか明確ではないという状況です。

[494] Web では文字コードの判定 (推定) が必要なのは初期の Webページが中心です。 既に二十年前後が経過しており、 自然言語の表記や語彙にも多かれ少なかれ変化が生じていると考えられます。 正書法改革が行われた言語もあります。 現在の Wikipedia から作成した言語モデルが当時の Webページに機能するかどうかは、 慎重にならざるを得ません。

[496] これについては、 UnivCharDet の系譜や chardetng を使った古い Webページの判定に顕著な劣化が見られないことから、 実用上の問題にはならなそうです。

[558] Wikipedia中文 (簡体字 / 繁体字), セルビア語 (キリル文字 / ラテン文字) で同じ記事データを別表現に自動変換しています。 元データは中途半端な混在の可能性が、 変換済みデータは不自然な表記の可能性があり、 取り扱いには注意が必要となります。

[509] Wikipedia は整った説明調の文章が多く、 会話文や俗語に乏しいことには特に注意が必要かもしれません。

[495] Unicode とそれ以外の文字コードとでは異なる符号化モデルを採用していることがあります。 特にアジアでは、地域で使われてきた文字コードフォントUnicode とが「文字」の概念のレベルで違っているケースが散見されます。 Wikipedia など Unicode のデータはそのままでは適用可能な言語モデルにならないかもしれません。 Unicode から当該文字コードへ変換することもできるでしょうが、 変換器の出力は当時の一般的な入力方法での利用実態と乖離していることも懸念されます。

[501] Unicode 以前の文字コードの文書では、 当該文字コードにない文字代用表記にしていたり、 文字参照で表したりしていることもあります。 こうした文書では通常の文章と違った文字の利用頻度となることがあります。

[502] どこの国でも初期の Webページでは英語が使われがちです。 英語と地元の固有名詞の組合せは、 地元の言語とも純粋な英語とも違った文字の利用頻度となることがあります。

[503] こうした事情を抱えた古い Webページは、 Wikipedia 由来の言語モデルをそのまま単純に使った判定では必ずしも正解を得られないようです。

[504] 注意が必要な具体的事例:

記号の扱い

ASCII文字の扱い

[305] EBCDIC などを除くと、ほとんどの文字コード体系は ASCII文字を共通に持っています。 そのため ASCII文字文字コードの判定に大きくは寄与しません。

[306] また多くのマーク付け言語プログラミング言語英語語彙を大量に含んでいます。

[307] HTML文書の場合要素名属性名JavaScript コード、 CSSスタイルシートなどの形で多くの英語ASCII文字表記を含みます。 更にいえば、初期の Webサイトはどの地域でも英語が多いです。 本文が英語でないとしても、話題がインターネット計算機の技術系のページでは英語英語由来の ASCII文字の語が極めて頻出します。

[315] 判定に使う文字の出現頻度の情報は、想定される自然言語の文章から計算されています。 英語の濃度が極度に大きいと、本来の自然言語の出現頻度から離れていき、 判定が狂う要因となってしまいます。

[308] こうした事情があるので入力の ASCII文字をどう取り扱うかは設計上無視できない問題となります。

[309] ASCII文字を無視すれば、こうした「ノイズ」も一気に除去することができ、 非ASCII文字文字コードの判定に注力し、関係ない部分の処理の負担を軽減できます。

[310] 一方で欧州言語などASCII文字言語の表記の主体となる場合、 非ASCII文字の割合が少ないので、 すべてのASCII文字を捨ててしまうと言語判定の重要な情報まで捨ててしまうことになります。

[311] 中間解として、 非ASCII文字の周囲の ASCII文字を判定に活用するのが現実的です。 ASCII文字非ASCII文字にまたがる n-gram の出現頻度は、 とりわけ欧州言語の判定に重要です。

[312] UnivCharDet非ASCII文字を含む単語を8ビット符号の判定に利用しています。

[313] chardetng はそれより攻めていて ASCII文字同士の連接は判定に使わないようです。

[314] ただしこうした戦略の違いがどれだけ判定性能や動作速度や消費メモリー量に影響を及ぼすのか、 定量的な比較はあまり行われていないようで不明瞭です。

[316] 欧州語で、しかもファイル名のような短い文字列が入力のとき、 ASCII文字だけの部分でも言語判定のヒントに使えれば、 数少ない非ASCII文字やその前後だけでは情報が不足する言語判定の補強材料になります。 しかし両者が関係ない単語の場合もあって、そのときは誤判定のリスクが増大します。

エスケープとの混合

[188] エスケープ (HTML文字参照など) とそうでない通常の文字が混合されている場合、 純粋な文字列とは違った文字分布になってしまう場合があります。

[189] 単純に ASCII文字だから、マーク付け言語の構文要素だから、 といった理由でエスケープを除去すると、通常の文字の前後関係が言語の一般的なパターンと外れてしまい、 判定に失敗することがあります。

[196] エスケープとそうでない文字が混在するのは、特に理由が無いこともありますが、 敢えて混在させていることもあります。文字化けしやすいとか、 その文字コードに存在しないとかです。得てしてそれらは文字コードの判定の際どい条件に関わってくる要素になりがちです。

欧州ラテン文字系文字コードの区別

[200] Windows-1252 (含 ISO-8859-1) と ISO-8859-2Windows-1250 は区別の難易度が高いことが知られています。

[201] そもそもラテン文字言語は文章の多くが ASCII文字で、 言語次第で少々の非ASCII文字が混ざるという構造です。 非ASCII文字主体の他文字言語よりも判別が難しいです。

[202] Windows-1252Windows-1250 は似た構造ですが、 収録される文字の種類は一部で著しく異なっています。 文章に少々混じる非ASCII文字のうちの更に一部の頻出文字が共通で、 残りが全く異なるので、バイトの並びとして見たとき、 どちらかにわかに判断しがたいことが多いです。

[203] Windows-1250ISO-8859-2 はだいたい同じで少し違います。 どちらも中欧でよく使われていた文字コードで、 同じような言語で同じように使われていて、 わずかな違いがどちらなのか判定するのが難しいです。

[211] MozillaUnivCharDetISO-8859-2 に対応しているものの、 ISO-8859-1 が誤判定されてしまうとして無効化されています。 UnivCharDet の派生の中にはこれを改善して有効化しているものもありますが、 それらも完璧に判定できるわけではありません。

[212] ced.hu ドメインのみ trigram を有効にするなど、 特別な処理で判定を強化しています。それでも Chrome はたまに判定を誤ります。 ロケール等による文字コード判定の補助

[213] chardetngTLD による傾斜など特別な処理で判定を強化しています。 それでも Firefox はしばしば判定を誤ります。 ロケール等による文字コード判定の補助

[214] 完璧な判定は困難ですから、どの手法を採るにせよ、最終的に文字コード指定メニューなど利用者が選択を覆せる手段が必須です。

[281] >>280ハンガリー語で書かれた HTML です。 各種の判定器は ISO-8859-2, windows-1250, windows-1252 と判断が分かれています。 Firefoxwindows-1252 と考えます。 Chromewindows-1252 と考えますが、 ced にバイト列を与えると windows-1250 と回答します。

[282] 実際には3符号化の共通文字が非ASCII文字でも多いのですが、 ハンガリー語であることとハンガリー語文字の使い方から ISO-8859-2 / windows-1250 と考えるのが妥当です。 >>280 の範囲では ISO-8859-2windows-1250 は同等です。

[283] FirefoxChrome文字コード指定メニューがないので、 文字化けしたまま修復できません。一種の不具合といえます。

[539] ラテン文字言語が多いので、 言語モデルは個々の言語ではなくいくつかまとめた言語群ごとに用意するのが無難です (>>485)。

[540] 西欧 (windows-1252 / iso-8859-1 / macintosh / ibm850 の類) のうち、言語 fo, is は利用文字が他と大きく違います。 chardetng西欧をこれらとそれ以外とで二分しています >>252。 定量的評価がなされているのかは不明ですが、 有効な戦略のようにみえます。

[541] 残りの西欧 (旧植民地等を含む。) は、 スカンジナビアドイツとそれ以外とでやや利用文字に異なりがあるので、 二分するのが一案と思われます。

[542] したがって西欧系の言語モデルは次の3種類にまとめられます。

[547] トルコ語西欧文字コードとよく似たものが使われますが、 微妙に違いがあり区別しづらいこと、大文字と小文字の扱いが違うこと (>>520) に注意が必要です。

[548] 中欧バルトはそれぞれの文字コード群に対応する言語群でモデルをまとめてしまって構わなそうです。 ISO-8859-3 は専らエスペラント語で使われたので、 これは独立させた方が良さそうです。

[552] et>>551>>554 に重出しますが、この是非については検討の余地があります。

[555] 他に ibm865ISO-8859-15, ISO-8859-16 などを扱うなら、対象言語に特化した言語群のモデルを用意したほうが良いかもしれませんが、 >>543 との関係を検討しなければなりません。

[556] なお各言語国家文字コードの利用実態についてはロケール等による文字コード判定の補助も参照。

[553] 越南語言語文字コードも性質が大きく異なるので、別途の扱いが必要です。

東側諸国の言語モデル

[525] 旧ソ連を中心とする東側諸国20世紀末に正書法改革やロシア語からの脱却を進めたところが多く、 その影響が問題となります。

[526] 初期の Webページ文字コードを扱うという目的で Internet Archive に残る各国の古い Webサイトを調査すると、地域によって差も大きいものの、

が大部分で、国単位の例外で

があります。ラテン文字に移行した国の旧キリル文字表記やロシア連邦の少数言語のキリル文字表記は見つけるのが困難です。 ロケール等による文字コード判定の補助

[536] 従って言語モデルの開発において旧正書法のデータを入手する必要性は大きくなさそうです。

[538] 現地言語のラテン文字表記について、何度か正書法の改定が行われている場合もありますが、 少なくても文字コードの判定に関係しては、特に目立った違いのようなものは検出できません。

[537] UnivCharDetキリル文字に関してロシア語の言語モデルを用意していました。 UnivCharDet の系譜の判定器の中にはこれだけでは不足としてブルガリア語の言語モデルを追加したものがあります。 Webページの判定ではこの2つのモデルがあれば他のキリル文字言語も含めて実用的な精度が得られるようです。

[523] chardetngアゼルバイジャン語について

  • For Azerbaijani, I replaced ə with ä to synthetize the windows-1254-compatible 1991 orthography.

なる調整をしたと説明しています。 >>252 しかしその必要性について十分な根拠を示していません。

[524] アゼルバイジャンにおける windows-1254 の利用は皆無ではないにせよ一般的とは言い難く、 ここで説明されているような表記法の利用実態があるのか、 それが判定精度に寄与するのか、といった点に不安が多いです。 実際に Internet Archive で確認できる古い .azWebサイト英語ロシア語か、 そうでなければ windows-1251文字参照の組合せであり、 >>523 のような事例は未発見です。 ラテン文字の文字コード, ロケール等による文字コード判定の補助

[522] chardetng蒙古語でも調整したと説明しています。 >>252 これは Encoding StandardFirefoxMNS 4330 の存在を認めていないために必要になる処置です。

大文字と小文字

[294] 出現頻度による手法の多くは大文字と小文字を同一視して文字n-gram の頻度を見ています。ほとんどはこの手法でうまく判定できます。

[520] トルコ語では他の言語と I / i大文字と小文字の扱いが違うので、 注意が必要です。

[521] 日本語平仮名片仮名は使われ方がアルファベット大文字と小文字とは違うので、 同じような取り扱いにするかどうかは悩みどころです。


[292] chardetng大文字と小文字の使い分けが自然なものを加点し不自然なものを減点しています。 >>291

[293] ギリシャ文字ですべて大文字にした語は、キリル文字をすべて小文字にした KOI-8ヘブライ文字と混同しやすいとされ、 cedchardetng が特殊処理を入れています。 >>291, >>284

ASCII 文字と非 ASCII 文字の隣接

[300] chardetngラテン文字とそれ以外 (ASCII文字とそれ以外) の隣接で非ラテン文字符号を減点しています。 >>299, >>252

[518] 日本でよく見る、ラテン文字の並びの途中にぽつんと漢字が1字混ざるタイプの文字化けの抑制になりそうです。

[519] この種の規則を組み込むには慎重な評価が必須です。 多バイト符号の第2バイトが ASCII英数字になることはよくありますし、 日本語などで漢字ラテン文字が隣接することは割とよくあります。

語長

[297] chardetng語長が23ならタイ文字以外を減点しています。 >>296

[298] chardetngEUC-KRハングル語長が5なら減点 (EUC_KR_LONG_WORD_PENALTY) しています。 >>296

[301] 他の実装にはあまり見られないので有効性は不明瞭です。

[302] johab は他の符号と誤認の排除にハングル語長の平均 (大雑把に 3 字くらい) との乖離の検知が有効なようです。

書字方向

[303] ISO-8859-8 など視覚順論理順の区別が必要となる場合があります。

[304] ヘブライ文字語末形語中形が異なるものが5字10種あります。 ISO/IEC 8859-8Windows-1255 はそれらに別のビット組合せを充てています。 UnivCharDetヘブライ文字列の先頭や末尾の字形の個数や前後から見た bigram の評価によってどちらか判定しています。

[253] chardetngヘブライ文字ではなくそれと併用される ASCII 句読点の使われ方で判定しているようです。 >>252

[557] どちらの手法も長短ありそうです。句読点法は、 題名やファイル名や短文などで機能しない虞があります。

越南語

半角カナ

実装

出現頻度等による実装

[176] 出現頻度等による実装:


[3] universalchardet は、 MozillaWebページの表示のために開発したものです。 多くのプラットフォームに移植されて使われています。

[4] 次の符号化に対応しています:

utf-8 utf-16be utf-16le iso-2022-cn big5 x-euc-tw gb18030 hz-gb-2312 iso-2022-jp shift_jis euc-jp iso-2022-kr euc-kr iso-8859-5 koi8-r windows-1251 x-mac-cyrillic ibm866 ibm855 iso-8859-7 tis-620 windows-1253 iso-8859-8 windows-1255 windows-1252

[6] データだけで未実装: iso-8859-2 windows-1250

[276] compact_enc_det (ced) >>91 は、 Google による文字コードの判定オープンソースライブラリーです。

[277] Google Chrome で採用されています。 Google 社内の検索Gmail などでも使われていると言われています。

[278] この種のライブラリーの中でも精度は高いです。 Google検索で使われる Google 社内の世界最大規模の Webページデータベースの解析の成果が反映されていると見られます。 ソースコード中でもいろいろな調整が入っている様子が窺えます。

[279] ただ逆に言えばオープンソースとはいえ Google 社員以外がこれを改善する改変を行うことは困難で、 そのまま使うか、他のソフトウェアの改善のヒントに使うくらいしかできません。


[82] CharamelPythonライブラリーです。機械学習によって Python が標準で対応するすべての符号化に対応したと謳っています。 >>36

[83] 実際に判定させてみると、他の判定器と比べて精度は今ひとつのようにも思われます。 その中には類縁の他の符号化と判断されたものがあり、 使用している文字次第でどちらとも判定できるので誤判定ではないと言えるものもありますが、 それらを除外しても不一致が多いように感じられます。 機械学習の方法による不透明なバイナリーデータを判定に用いているため、 改善も困難と思われます。

[85] 付属の試験データ >>31 は実際の Webページらしきものやその他のテキストデータが含まれますが、 各文字コードには機械的に変換したものと見られます。 中には非ASCII文字が1つも含まれないデータしかない符号化もあり、 試験データとして精査されたものとは思えません。

符号構造のみによる実装

[54] 符号構造のみによる実装:

[76] LV Homepage (in Japanese), , https://web.archive.org/web/20010119052900/http://www.mt.cs.keio.ac.jp/person/narita/lv/index_ja.html

現在の自動選択の方法は簡単なものです. ファイルを先頭から読み込んでいって, 8ビット目が立っている文字があった場合, 『その行』の中で euc-japan で使用される領域のみを使っていれば euc-japan, そうでなければ shift-jis です. つまり,『漢字らしきものを含む最初の一行』で判断しています. 8ビット目が立っている文字が見つからなければ いつまでも自動選択のままの状態が続き, 判断が必要になったときに判断します. shift-jis の片仮名のみを使用した場合や, 運の悪いときは, 誤って euc-japan と認識されます.

[70] Add UTF-7 to replacement encoding list? / Encoding sniffing · Issue #68 · whatwg/encoding, https://github.com/whatwg/encoding/issues/68

[71] Encoding: make it clear sniffing for UTF-8 is not acceptable by annevk · Pull Request #14455 · web-platform-tests/wpt · GitHub, https://github.com/web-platform-tests/wpt/pull/14455

[199] Escape Codec Library: ecl.js, , https://www.junoe.jp/downloads/itoh/enc_js.shtml

[198] ISO-2022-JPとSJISとEUCJP(とUTF-8)をざっくり判別するアルゴリズム - うならぼ, https://unarist.hatenablog.com/entry/2017/02/28/205401

テストデータ

関連

[55] 文字コード選択メニュー

メモ

[80] How to reliably guess the encoding between MacRoman, CP1252, Latin1, UTF-8, and ASCII - Stack Overflow, https://stackoverflow.com/questions/4198804/how-to-reliably-guess-the-encoding-between-macroman-cp1252-latin1-utf-8-and

[49] [ptexenc] 入力ファイルの文字コードの自動判定 · Issue #142 · texjporg/tex-jp-build, https://github.com/texjporg/tex-jp-build/issues/142

[89] 21990 – When a rare EUC-JP character is present, explicitly (and correctly) labelled EUC-JP document is mistreated as Shift_JIS, https://bugs.webkit.org/show_bug.cgi?id=21990

[90] 16482 – Hook up ICU's encoding detector and add a boolean param to Settings and WebPreferences, https://bugs.webkit.org/show_bug.cgi?id=16482