変換表

文字コード変換

[6] 文字列をある文字コードから他の文字コード変換することを、 一般には文字コード変換といいます。

呼称

[16] 文字コードを変更する操作 (演算) は一般に変換, convert, conversion のような語で表すことが多いようです。

[17] 関数名等としては from や to と文字コードの名称をつなげるなどして組み立てることが多く、 変換操作の動詞を含まないこともしばしばあります。

[322] Python では変換器を codec と呼んでいます。この言葉は媒体符号化の分野で符号化方式やその実装に言及するときに使いますが、 文字符号化の分野ではほぼ使わず、おそらく Python 界隈だけの独特の風習です。

[324] 文字コードの変換とよく似た、ときに混同され、ときに併用・連結される処理に escape / unescape があります。

符号化と復号

[13] バイト列文字列データ型の値に変換することを復号文字列データ型の値からバイト列に変換することを符号化といいます。

[14] 文字列に限らず、プログラミング言語等の処理用の内部データと保存用の外部データの変換を一般に符号化復号のようにいいます。
[323] 符号化 (encoding) はバイト列への変換の操作の意味 (動詞) でも、 文字符号化の方式や技術の意味 (名詞) でも、 文字コード規格への文字の追加の意味 (動詞) でも使います。 文脈によるので注意が必要です。

[15] 現代のプログラミング言語等では内部データを Unicode文字列とすることが多いので、 Unicode への変換を復号Unicode からの変換を符号化のように言っているかのような使われ方をされがちですが、 厳密な語法ではありません。

転符号化

[4] 文字列をある文字コードから他の文字コード変換することを、 転符号化 (transcode) といいます。 転符号化するソフトウェア部品を、 転符号化器 (transcoder) といいます。>>1

[5] あまりメジャーな語ではありません。

[1] RFC 3536 - Terminology Used in Internationalization in the IETF ( 版) http://tools.ietf.org/html/rfc3536#page-6

Transcoding is the process of converting text data from one character encoding form to another. Transcoders work only at the level of character encoding and do not parse the text. Note: Transcoding may involve one-to-one, many-to-one, one-to-many or many-to-many mappings. Because some legacy mappings are glyphic, they may not only be many-to-many, but also discontinuous: thus XYZ may map to yxz. <CHARMOD>

In this definition, "many-to-one" means a sequence of characters mapped to a single character. The "many" does not mean alternative characters that map to the single character.

[2] RFC 6365 - Terminology Used in Internationalization in the IETF ( 版) http://tools.ietf.org/html/rfc6365#page-7

[3] Character Model for the World Wide Web 1.0: Fundamentals ( ( 版)) http://www.w3.org/TR/charmod/#sec-Transcoding

変換の意味

[173] 文字コードの変換の操作は、文字列意味に影響を与えることがあります。

[174] UTF-8UTF-16 の変換のように同じ文字の定義を共有する文字コード体系同士の変換であれば、 技術的な表現方法の機械的な変換にとどまりますから、文字意味には影響が及びません。

[175] UTF-8EUC-JP の変換のように異なる符号化文字を扱う文字コード体系同士の変換だと、 変換によって文字意味に影響が出てしまうことがあります。 例えば UTF-8 (Unicode) のEUC-JP (JIS X 0208) のは、 厳密には意味が異なります。変換はこの微妙な意味の違いを無視する操作となります。

[176] 変換の一方の文字コードにのみある文字が含まれる場合など、 「微妙な意味の違い」を超えた無視できない違いを生じる場合もあります。 詳しくは本記事の各項を参照。

[177] >>175 >>176 の要素を含む文字コードの変換は、破壊的な操作です。 無闇矢鱈と行うべきではありません。

異なる文字への変換

[301] 様々な歴史的事情によって、意図的に別の文字への変換がなされることがあります。

変換のインターフェイス

[19] 文字コードの変換の操作は次のような形態のいずれかで実装されていることが多いようです。

[29] >>18 >>22 >>23プラットフォームAPI として提供されていることもあれば、 アプリケーション等が独自に実装する場合も多いです。

[32] >>18 >>24 は独立したプログラムとなっている場合が多いです。 >>18 はこの場合テキストファイルの変換操作と言えます。

[33] >>28Webページtextarea 等を使った変換ツールとして提供されているものです。 それと同等のライブラリーとして提供されている場合もあります。 8ビット符号の変換器を称していても、 実態は8ビット符号から変換された Unicode文字列同士の変換になっています。

[34] >>24 >>26Webブラウザーワープロ等のソフトウェアの内部で動作するブラウザー拡張マクロのような形になっているものです。 文字列だけでなくフォント名が変換に関与します。

[41] 入出力の文字コードは、多数に対応しているものもあれば、 特定のものに限られている実装も多いです。 ただし、 多数に対応しているものでも、古今東西の膨大な数の文字コード技術の中の限られた一部のものにしか対応できていません。

[43] 多くの実装は対象となる地域や言語に関係する文字コード群にしか対応していません。 20世紀には当該システムで採用している文字コードを中心に、 それとその他の文字コードとの変換という形態が多く見られました。 21世紀になると Unicode を中心に、 それとその他の文字コードとの変換という形態が一般化しました。

[42] 実変換処理の定義を宣言的な外部ファイルに分離するなど拡張可能な設計になっているものもありますが、 そうでない固定のものもかなり多いです。 拡張可能なものであっても、外部ファイルの記述力には限界があり、 複雑な変換は実装できません。

[46] 狭義の文字コードの変換の他に、

... するものもあります。

[137] 文字コード体系によっては、同じ文字の表現方法に揺れ、曖昧性、その他本記事の各項に挙げるような選択肢が存在することがあり、 それらを動作オプションとして指定できるものもあります。

[45] 変換不能な場合にエラーとするか、 escape 等に置き換えるか、 代替文字に置き換えるか、置換文字に置き換えるかなどを動作オプションとして指定できるものも多いです。

[44] GUIワープロソフトウェアのマクロのような形態のものは、 動作オプションやエラーの処理などを通知したり、確認したり、選択させたりしているものもあります。

[48] HTMLRTFワープロソフトウェアの独自形式のファイルなど、 単純バイト列テキストファイルとしての変換処理ができず、 ファイル形式フォント名に応じた処理が必要となる場合があります。

[178] JavaScript によって DOM 上に挿入された文字列のように、 HTML ファイルの静的な変換ではなく >>26 方式で実装された変換器が必須となる場合があります。


[271] 実務上は、処理を途中で停止したり、中断・再開したり、 ネットワークから追加のデータが到着するごとに続きを処理させる形態にしたり、 といった仕組みが必要になることがあります。 (>>269 も参照。) こうした措置をプログラミング言語の組み込みのデータ型や制御機構で自然に実現できる場合もあれば、 独自の設計が必要となる場合もあります。

[275] 入力が1バイト符号だけならバイト列を分割して読み取って変換器の入力としても、 出力を単純につなぎ合わせるだけで済みます。

[276] 入力に複数バイトの符号が含まれたり、状態を持つ文字コード体系だったりすると、 分割して変換した出力の連結は全体の変換結果と一致しないことが多くなります。

[273] 入力が空文字列や数バイトの短いバイト列であることもあれば、 数GBの長大なバイト列であることもあります。 こうした普通でない入力に対する設計や動作検証はなおざりにされがちなようで、 不具合を抱えている実装の事例も散見されます。 セキュリティーの問題となることもありますから、 注意が必要です。

[274] 利用形態によっては、例えば短い文字列と決まっているから長大なバイト列で性能が低下しても許容できる、 とか一定以上のバイト数なら処理を拒絶する、といった実装戦略もあります。 しかしそうした場合であっても、セキュリティーは常に注視が必要です。

変換の入出力のデータ型と文字コード

[138] 現代の多くのプログラミング言語にはバイト列文字列の区別があります。 また、 HTMLRTF やその他の文書形式データ構造は、 文字列を構成要素としていることが多いです。

[139] 組合せ上、文字コードの変換の処理の入出力は

... の4通りがあり得ます。

[181] 言語情報, bidi, フォント, 装飾, その他の付加情報の付随する構造を考慮すると、 組合せは更に増えます。

[182] なお、 RTFフォント依存符号化フォントが指定された文字列のように、 文字列データ型の仕様上の文字コードとその実際の文字コードは一致しないことがあります。

[183] 古典的な文字コードの変換の実装や、単独の文字コード対の実装は、 単純にバイト列からバイト列への変換としていることが多いです。 複数の文字コード対に対応するときは、 単体の変換の実装を増やしていくことになります。

[184] 多数の文字コードの変換に対応する実装は、 特定の文字コードを「中心」に、それと各種文字コードの変換を実装する形態としていることが多いです。

[185] 現代の実装はプログラミング言語文字列型文字列を「中心」 とすることが多いです。現代のプログラミング言語文字列型文字列は、 ほとんどの場合 Unicode文字列です。従って Unicode からの変換、 Unicode への変換ということになります。

[186] 古くからの実装は、 Unicode ではない内部符号を使っていることがあります。 新しい実装であっても多くの文字コードに対応する実装は独自の内部符号を採用していることがあります。

[193] 完全独自の内部符号ではなく、 PUA を使ったり、 文字に付随する文字とは別の情報を組合せたりすることもあります。

[159] 実装の都合 (例えば多数の文字コード体系の相互変換のための中間符号としての利用) のために、 入出力の文字コードと無関係の文字コードを利用する場合があります。

[160] 中間符号は、入出力として現れにくいものが選ばれますが、 設計者の判断と利用者の用途が一致するとは限らず、 意図しない結果になる場合もあります。

[164] ワープロマクロとしての変換器の実装は符号位置フォントの組を変換の入出力に使うことがあります。 同じ符号位置でもフォント名が違えば別の文字扱いになり、 変換対象にしたりしなかったりできます。 実在しないダミーのフォント名を使ったり、 実在のフォント名であっても多段変換のどの段階で変換するかを調整することで、 中間符号を用いる方式と同様の効果を実現している場合があります。

[187] 内部符号等が扱う Unicode だけでない情報の例 文字のようなもの


[188] 現代のほぼすべてのデータ (過去からデータ形式の変換により引き継がれたものを含みます。) は8ビットバイトで記述されています。

[189] 7ビット符号は多いですが、実際の処理では8ビットバイトの列として扱うことになります。

[190] 歴史的には6ビットや9ビットなど異なる単位もありますし、 バイトより大きな単位が実処理の最低単位となることもありますし、 プロトコルデータ形式が8ビット以外の単位で区切っていることも多々ありますが、 現代の計算機上の文字コードの処理の入出力となる段階では8ビット単位となっており、 変換の処理がそれ以外を想定することはまずありません。

[191] 理屈の上では16ビット符号や32ビット符号など、より大きな単位を束ねた符号体系もいくつもありますが、 それらも結局のところは符号空間の大きさに過ぎないのであって、 それが最低の処理単位となるプラットフォームは極めて稀です。 よって、壊れたデータが端数のバイトを持つ場合や、 ネットワーク転送時のパケット境界が文字の途中で出現する場合などを文字コードの変換の処理は考慮する必要があります。

[192] Base64application/octet-stream のように端数のビットを扱える (扱えてしまう) (こともある) データ形式もありますが、 それを文字コードの変換の実装と組合せて利用できる実装はほとんどありません。


[194] C言語など NULL文字列終端とするプラットフォームもありますが、 そうしたプラットフォーム文字コードの変換の実装であっても、 入出力のバイト列の途中で 0x00 が出現する文字コード体系や、 入出力の文字列NULL を含む場合を適切に処理できる必要があります。

[195] UTF-16ASCII文字を使うと 0x00 バイトが出現します。

文字コードの指定

[232] 入出力の文字コードの指定は、 文字コードの識別子を明示的に与えるものもあれば、 関数名等に組み込まれているものもあります。

[233] 入出力の各種の動作オプションまで含めた文字コードの名称・定義を提供しているものもあります。

[47] 入力となる文字コードの明示的な選択を省略し、 文字コードの判定に拠るものもあります。

[270] その場合、応用に判定結果を通知する必要があるかもしれません (>>269)。

[234] HTMLRTF など、文書形式自体に文字コード決定の機構が組み込まれているものもあります。 HTMLにおける文字コード それを尊重するものもあれば、 敢えて無視するもの、 上書きして指定できるものもあります。

[272] 処理の途中で文字コードの指定を検知して、その場で入力文字コードを切り替える仕組みが必要となる場合もあります (>>269)。

[235] フォント依存符号化を使った HTMLRTF のように文字コードの指定が多層的になる場合もあります。 通常は上層は記述されたフォント名により自動的に決まりますが、 上層まで含めた全体の文字コード名が指定された場合の処理、 上層のフォント名が実態と合っていないため無視して上書きする動作オプションの提供など、 実装上考慮するべき特殊ケースもあります。

[236] 原理的に RTF で使いようがない文字コードなど、 文字コードの指定と文書形式の指定が矛盾する組合せが存在するため、 実装上は注意が必要になります。

変換可能性の判定

[145] 文字コードの変換の操作と関係して、文字コードの変換が可能かどうかを決定するという操作が必要となる場合がたまにあります。

[149] こうした機能は実際に変換を行いながらエラーを検出する形で実装される場合もあれば、 事前に高速に判定する手法により実装される場合もあります。 どちらが好ましいかは使い方によりますが、後者の場合は実際の変換処理と乖離するリスクがあります。

変換の構成要素

[325] 変換処理の実装戦略は様々ですが、その性格はいくつかの要素に分解できます。

1対1対応

[50] 一対一対応が存在する場合、入力を機械的に変換して出力するだけですから、 変換器は非常に単純になります。

[51] すべての符号一対一対応可能な文字コード体系間の変換は、 宣言的な変換表を用意して外部化することで、容易に対応体系を増やすことができます。

[49] 平成時代初期くらいまでに欧米企業が進出していた地域の標準的な文字コード文字のほとんどは、 Unicode一対一対応が存在します。 また、同じ地域の同じ言語を対象とする他の文字コードとも1対1対応が存在する文字が多いです。

1文字対多文字対応

[52] ある文字コードで1文字として扱えるものが、他の文字コードでは複数の文字の列になることがあります。

[53] 例えば地域の文字コードではアクセント付きで1つの文字で表せたものが、 Unicode では基底文字結合文字の組合せとして表現するほかないことがあります。

[54] 一対一対応限定の実装より少し面倒になりますが、容易に実装できます。

[59] 西暦1990年代初頭頃に欧米企業が実装していなかった文字コードにある文字は、 それが他の Unicode文字の組合せで表現できると判断された場合 Unicode に追加されないことがほとんどなので、 それらから Unicode への変換でこのパターンとなります。

多文字対1文字対応 / 多文字対多文字対応

[55] ある文字コードで複数の文字の列で表されるものが、他の文字コードでは1文字となることがあります。

[56] 複数の文字のそれぞれが変換先の文字コードにも存在するなら、 1対1対応とみなして単純に変換することも可能です。

[57] 複数の文字のいずれかが変換先の文字コードに存在しないなら、 文字列を1文字に変換する必要が出てきます。

[58] 例えば基底文字結合文字の組合せが入力されたとき、 結合文字に相当する文字が変換先にないなら、 組合せと同等の文字に置き換えないといけません。

[61] 入力があってもすぐに変換先を確定できず、数文字読み続けて適切な出力文字を選ぶ必要が出てきますから、 変換処理は複雑になってきます。

[60] 1文字対多文字対応の変換の逆方向でこのパターンとなります。


[62] ある文字コードで複数の文字の列で表されるものが、 他の文字コードでは複数の文字の列で表され、 両者の構成する文字それぞれに直接的な対応関係がないことがあります。

[63] インド系文字の文字コードなどと Unicode との変換のように、 なにを符号化文字の単位とするかの考え方に大きな違いがある場合に、 この種の変換が頻出することになります。

[64] 変換処理は相当に複雑になります。

文字の削除

[257] 入力文字コードの文字(列)が完全に削除されることがあります。

変換対象部分文字列の条件

[277] ある文字コードの文字(列)から他の文字コードの文字(列)への変換が、 入力文字コードの文字(列)の複雑な条件という形で記述・実装されることがあります。

[278] 汎用の正規表現エンジンで実装されることもあれば、 独自の状態機械などの形で実装されることもあります。

[279] 固定長ではない文字列になることもあります (+, * など)。

[280] 記述には文字クラスが使われることもあります。 文字クラス否定が使われることもあります。 1文字だけの小さな文字クラスもあれば、 十数万字の大きな文字クラスになることもあります。

[281] 記述には選択 (|) が使われることもあります。

[283] 記述形式によっては文字列空文字列文字クラス要素とできることがあります。 実効上は選択と等価です。

[282] 文字クラスや選択は基本的には複数の置換規則に展開できます。 ただ文字クラスの展開は文字数が多すぎて現実的でないこともあります。

変換対象部分文字列の可変部分の置換

[286] 対象部分文字列のうちの一部分が、置換先で使われることがあります (capture)。 実例をみていくと、

  • [287] 順序の入れ替えの規則の記述に使われている場合
  • [288] 前方一致や後方一致の条件として記述されている場合
  • [289] ある文字の前後で使われる文字列の組合せを置換したい場合

のような用途があるようです。

[291] 順序入れ替えのように、置換前と置換先とで出現順序が変わることも少なくありません。

[292] 1つの変換の条件で出現する capture の数は1個、2個程度のものが多く、 5,6個に達するものは珍しいようです。


[290] 対象部分文字列が文字クラスや選択で記述されているとき、 置換先では写像からそれに対応付けられた文字を選ぶ形になっていることもあれば、 部分文字列だけに適用される規則群の形で記述されていることもあります。

変換対象部分文字列の前方一致と後方一致の条件

[98] ある文字コードの文字(列)から他の文字コードの文字(列)への変換が、 入力文字コードの文字(列)とその前後の文字(列)の条件という形で記述・実装されることがあります。

[99] 単純な多文字からの変換に置換できる場合も多いですが、 それでは変換表が巨大になりすぎてしまう (数千から無限大の規模となる) とき、 先読み後読みのような手法で実装されることになります。

[100] 条件部は固定長ではない文字列になることもあります (+, * など)。

[262] 条件部の記述には文字クラスが使われることもあります。 文字クラス否定が使われることもあります。 1文字だけの小さな文字クラスもあれば、 十数万字の大きな文字クラスになることもあります。

[284] 条件部の記述には選択 (|) が使われることもあります。

[285] 記述形式によっては文字列空文字列文字クラス要素とできることがあります。 実効上は選択と等価です。

[101] 条件部は文字列先頭、文字列末尾、語境界空白といった形で記述・実装されることも多いです。 こうしたものは語頭形語末形のような文字列中の文字の位置によって変換を制御する必要がある場合に使われがちです。

[260] 条件部は一致するべき条件が記述されることもあれば、 一致しないべき条件が記述されることもあります。

[261] 一致する、しないの条件と先頭・末尾・境界が一致するかしないかは、 条件の記述方法や正規表現エンジン等の利用方法によって変わってくるので要注意です。

メモ

[102] いずれの場合も変換処理は相当に複雑になります。

[103] 意図を正確に記述できているのか疑問が残る実装も散見されます。

[104] >>101 のように本来同等の条件を意図していたと思われるものが実装ごとに違った形で記述されていることもよくあります。 同じような条件を移植していても、プログラミング言語等で挙動が微妙に異なると思われるケースもあります。

[263] 文字クラスの定義、 境界の定義、 非決定的な挙動などが実装手法によって変わりがちです。

[105] 実装の正しさの評価や他の実装との比較が困難になりますから、 可能であれば避けるべきなのでしょうが、避けることが困難と思われるケースも多いです。

1対多対応

[65] 入力文字コードの1つの文字に相当する出力文字コードの文字(列)に複数の候補がある場合があります。

[66] 出力の文字(列)が互いに同等なら、どれを選ぶかは実装者の任意の選択となります。 ライブラリーの類なら応用に動作オプションとして指定させる場合もあります。 プラットフォーム等で慣習が成立している場合もあります。 慣習とならず相互運用性の問題が起こることもあります。

[67] Unicode への変換で、 基底文字結合文字の組合せを出力するか、 合成済み文字を出力するかの選択肢が存在することがあります。 一般的には合成済み文字が適切と考えられていますし、 1対1対応の変換にできるので実装も単純化できます。

[68] Unicode に似た字形の記号がいくつもあり、 いずれを選択するか実装によって違いがあって相互運用性の問題となっている場合があります。

[70] 出力の文字(列)が同等とはいえないなら、どれを選ぶかは文脈その他によって決めることになります。

[71] 前後の文字列との組合せで決められるなら多文字対多文字対応のような形で変換できますが、 多くの場合は機械的に選択することが困難です。

[72] 機械的に決定できなければ、不適切な場合があることを承知の上でどれかを選ぶか、 GUI による選択などの手段で個々に決めるなどの方法になります。

[73] 多くの場合はデータの劣化を恐れずどれか1つを選んで機械的に対応付ける方法を採っているようです。

[74] そうした関係性によって旧来の文字コードにおける文字の弁別や選定の慣習が Unicode にも持ち込まれている事例が多々あると思われます。 元々旧来の文字コードは当該地域や言語の文字の選択の慣習を反映して開発されているでしょうから、 Unicode がそれと異なる基準で文字を収録していたとしても、 Unicode の基準が現地の運用に耐えられるとは限らないわけです。

[69] 実装ではなく Wikipedia の記事の対応表などでは、 複数の対応先の候補を併記する形を採っていることがあります。

多対1対応

[75] 入力文字コードの複数の文字に相当する出力文字コードの文字が1つとなる場合があります。 いくつかに分類できます。

[79] >>76 >>77 は実装上は大きな問題はなく、単純に変換すれば済みます。

[80] ただし、往復変換はできなくなることに注意が必要です。

[81] >>78Unicode から従来の文字コードへの変換で実装されていることがあります。 best fit などと呼ばれることがあります。 変換で失われる情報をできるだけ少なくするための配慮として実装されたものでしょうが、 情報の損失がわかりにくくなるという問題があります。

[84] 例えば Unicode からシフトJISに変換する Win32 APIbest fit を有効にすると、アクセント付きラテン文字がただのラテン文字に変換されます。

[82] また、 best fit 写像によってセキュリティーの問題が生じる事案も報告されています。 best fit

[83] best fit は提供する場合でも動作オプションとするべきでしょう。

多段変換

[85] インド系文字の文字コードUnicode との変換など符号化文字の考え方が大きく異なる文字コード間の変換は、 1パスの単純な変換ではなく、複数回の変形処理の組合せとして実装されていることがよくあります。

[86] 多段変換とすることで記述や実装が単純化することも多いですが、 どんな入力がどんな出力になるかの見通しは悪いことが多いです。


[156] 機能的必然性がなく、実装の容易性のために多段化されているとみられる事例も散見されます。 例えば JavaScript で実装された変換器で、 Stringreplace メソッドを繰り返し適用することで順次文字を置換していくようなスタイルの変換器が多数あります。

[157] 一昔前なら何パスも掛けて文字列を走査するのは無駄が多く、 1パスで文字列の先頭から順に見ていくのが良いとされたのでしょうが、 現在ではパフォーマンス上の違いは微々たるもので、 実装の簡単性と視認性のメリットが遥かに上回ります。 標準メソッドだけで記述できる分、独自にループで1パスで実装するよりも高速になる可能性までありますし、 実装ミスによる不具合のリスクも減ります。

[158] ただしこの手法は入力文字集合と出力文字集合が重ならない場合には安全ですが、 そうでない場合には置換の適用順序が重要になってきます。 ところがこれをあまり意識していないと思われる事例が散見されます。 出力が再度入力条件と一致することによる置換同士の相互作用が意図的と推定できる場合もあれば、 意図的なのかどうかはっきりしない場合も多いのです。 どちらにしても変換処理全体の入出力の関係が不明瞭となりがちで、不具合の温床となっています。 開発者自身が挙動を理解しきれず冗長な置換規則や誤った置換規則を挿入したと思しき事例もあります。


[253] 変換の段 (pass, phase) 数は、 前述の通り実装戦略次第で1パスだったり数十から数百パスだったり様々となります。

[254] 前述の replace 鎖のような実装戦略の場合、 互いに干渉せず同時処理可能なものをまとめると高々二、三十パス程度になることが多いようです。 (もちろん一パスから二、三パス、せいぜい十パス程度に収まるものがかなり多いです。) 中には百数十パスくらいまでしか減らないものもありますが、 同時処理可能かどうかの判定が難しいケースを保守的に数えた結果ですから、 厳密に検討していけばもっと減らせるでしょう。

[255] TECkit.map のように同時処理可能なものをまとめつつ人間の判断で分けた方が扱いやすいものを分ける実装戦略だと、 多くても十数パス程度になるようです。 .map 人間により .map で記述されたことがない文字コードの方が圧倒的に多いでしょうが、 おそらくこれを大きく上回る複雑さの文字コードは存在していないでしょう。

正規化処理

[87] 変換処理本体の単純化のために別段で正規化の処理が適用されることがあります。

[88] 入力が Unicode のとき、 NFCNFD を適用することで、 変換処理本体が基底文字結合文字の組合せか合成済み文字の一方だけを記述することで済ませたり、 結合文字の順序が入れ替わっている場合への対処を省略したりすることができます。

[89] 入力が翻字系の符号化のときで大文字・小文字不区別のとき、 一括してどちらかに統一してから変換処理本体を実行する場合があります。

[90] 入力の文字コードの構造上の理由から変換処理本体が基底文字結合文字の組合せを出力したり、 合成済み文字を出力したりすることがあり、これを好ましくないと考える場合、 変換処理本体の後に NFCNFD を適用する場合があります。

[91] こうした処理は変換処理本体の実装コストを削減できますが、 思わぬ副作用を生むこともあり、注意が必要です。

[92] 大文字と小文字の変換はプログラミング言語によって実装が違いますし、 ロケールによって違った変換となる場合もあります。 変換器の実装者がそうした詳細まで細かく注意して設計していないと思われる事例が散見されます。

[256] なお、 Unicode正規化を実装しているかのような説明でありながら、 当該文字コードやその言語に関連がある部分だけしか実装していないようなものもあります。

順序入れ替え処理

[93] インド系文字の文字コードUnicode の変換など、 符号化文字の考え方が大きく異なる文字コードの変換では、 入出力で文字の配列順序を変更しなければならないことがよくあります。

[94] 入力または出力の数文字の部分文字列が特定のパターンに一致するかを判定し、 一致するなら順序を入れ替えるような処理を複数種類適用することになります。

[95] 部分文字列の組み合わせ数が膨大過ぎて、 多文字対多文字対応に展開するのが事実上不可能な場合が多いです。

[96] 実装によって、 出力文字コードへの変換後に正規表現の置換を組合せたり、 正規表現の置換の組合せの後に出力文字コードへ変換したり、 出力文字コードへ変換しながら順序を入れ替える状態機械を実装したり、 内部処理用の中間表現を導入したりと、 様々な実装戦略が採られています。

[97] どの方法も相当複雑になりますし、概略同じ手法でも置換パターンの作り方次第で見かけ上の処理は大きく変わってしまいます。 従ってソースコードを読んで挙動を理解するのは至難の業ですし、 諸実装の特徴を比較することも困難です。


[242] ヘブライ文字の文字コードアラビア文字の文字コードでは論理順視覚順の変換処理が必要となる場合が多いです。

[243] 従来文字コード側だけでなく Unicode 側にもbidi 関係の制御の文字が多数あり、 それらの相互作用を考慮する必要があります。

[244] の単位を検出し文字の順序を入れ替える必要があります。

[245] 単純な逆順への入れ替えだけでなく、結合文字のように基底文字に付随する文字をグループ化するなど細かな調整が必要となります。

[247] 実装戦略としては、

  • [248] 狭義変換の前に実施する、
  • [249] 狭義変換の後に実施する、
  • [250] 狭義変換と組み合わさった処理として同時に実行する、

... の3通りがあります。 完全に分離する方がアルゴリズムとして綺麗でありメンテナンスが容易で不具合を生みにくいと考えられますが、 1パスで処理したい、 >>244>>245 のような複雑な処理との組合せで >>250 の方が実装しやすいという状況もあるのでしょう。

[246] TECkit.map のように事前または事後に行うべき処理として分離している記述形式もあります。

[251] 文字コードの変換の処理から視覚順論理順の処理を敢えて除外することもあります。

[252] Webにおける文字コードの処理では視覚順ISO-8859-8Unicode文字列変換する際に論理順にせず元のままにして、 CSS によって表示する際に通常の bidi の規則を適用せずそのままの順序としています。 ISO-8859-8

アルゴリズム的変換

[109] 文字コードの変換の処理で変換表の類によらない計算のみで実現できる操作はいろいろあります。 例えば、


[106] 平成時代初期ごろまでの日本文字コードの変換は、 JIS X 0208 に基づくシフトJIS日本語EUCといわゆるJISコードの変換が中心で、 簡単な計算で相互に変換できました。

[107] その後 Unicode の普及期には、計算で変換できず数千行の変換表が必要となる Unicode と従来文字コードとの変換は敬遠されがちで、 Unicode 移行の障害となっていました。

[108] Unicode が一般化した現在では、計算機性能の向上もあってこうした問題が意識されることはなくなりました。 しかしシフトJIS日本語EUCといわゆるJISコードの変換も Unicode を介して行われることが増え、 Unicode との対応関係がそれぞれ微妙に異なるために従来なら無劣化で行えた変換でデータ損失が生じるような事態も生じています。

入力符号の状態

[204] 文字コードの変換の実装は、入力を読み込みながら、 入力文字コードの状態を管理する必要があります。

[205] 入力の状態の例:


[212] Unicode正規化から呼び出される正準再順序付けのように無限の長さの文字列の状態を保持しなければならない処理があります。

[237] TECkit.map+* などによる任意の長さのを条件として記述していますが、 一致する最大長を 15 以下に制限しています。

[238] 同じような条件の記述でも、このような長さ制限を設けていないで正規表現の実装に任せたり、 自前でも状態機械により無限の長さを受理したりする実装も多いです。

[239] 自然言語の記述である文字の列であれば、そうした余りに長い文字列が出現することはまずないと考えられるので、 15 など現実的な長さに制限することは合理的な実装上の配慮であり、セキュリティーその他のために望ましくもあります

[240] こうした長さの上限のない変換条件の記述は、 音節の単位を検出しつつ置換や順序入れ替えを実施したいインド系文字の文字コードの変換処理や、 単位で順序の入れ替えを実施したいヘブライ文字の文字コードアラビア文字の文字コード論理順視覚順の処理、 語頭形語中形語末形の処理などで見られます。

[241] 文字コードの変換以外では shaping結合文字列など文字のレンダリングの関連の処理で似たような状況が出現します。

[267] 実装によっては文字コードの判定翻字転写の体系とただの English の区別の確立的判定など、符号化手法を判断する処理と変換の処理が結合していることがあります。

[268] 更には HTML文字コード指定の検知のような文書形式プロトコルに依存した処理も併合されていることがあります。

[269] HTML における符号化の変更の処理など、文字コードの判定の結果を報告したり、 それに応じて文字コードの変換の処理を再起動したりといった措置が必要になることもあります。

状態付き符号への変換

[150] 出力文字コードが状態を持つことがあります。

[151] 例えば出力が ISO/IEC 2022 に基づき複数の符号化文字集合を切り替えて使える符号の場合、 変換は入力文字符号から状態と文字符号の組への写像の形で記述されることになります。 出力の状態が現在の状態と同じならそのまま文字符号を出力でき、 現在の状態と異なるなら状態を切り替えてから文字符号を出力することになります。

[152] 同じ入力文字符号に対する出力の候補として、複数の状態とその文字符号がある場合があります。 この場合、現在の状態と同じものがあれば、その候補を選択すると、自然な出力となる場合が多いです。 (出力文字コードの仕様や慣習にも依存します。)

[153] 例えば Unicode から ISO/IEC 2022 に基づき JIS X 0208GB 2312 を使える符号へと変換する場合、 JIS X 0208GB 2312 のどちらにも存在するので、 現在の状態に応じてどちらを使うかを決めれば、切り替えのための指示シーケンスを節約できます。

[154] 出力の状態切り替えは、 ISO/IEC 2022エスケープシーケンスのような文字コード層の技術による場合もあれば、 HTMLfont 要素RTFフォント切り替えのような文書形式の機能によって行う場合もあります。

[155] エチオピア文字の文字コードチベット文字の文字コードギリシャ文字の文字コードなど、 変換処理が入力のフォントの違いを認識したり、出力のフォントを切り替えたりする必要のある構造のものがあります。

変換されない部分文字列

[326] 入力の部分文字列がそのまま出力されることがあります。

[327] 入力文字コードと出力文字コードで同じ表現になる文字がある場合があります。

[328] 多くの文字コード体系は ASCII文字を同じバイト列で表します。

[329] 入出力がほぼ同じでも、バイト列文字列のようなデータ型の違いだけがある場合があります。 そうした場合の処置はプラットフォームによって異なります。

[330] 暗黙の型変換が行われるプログラミング言語等もあります。 そうした機能性を活用するときは意図せぬ型変換による不具合への注意も同時に必要となります。
[331] UTF-8バイト列から内部表現 UTF-8文字列へ、 通常行われる検査を省略して高速に変換する API を持つプログラミング言語等もあります。 そうした機能を使う場合、不正な入力を無理に文字列型キャストしてしまい、 以後の処理が誤作動したり、暴走したり、セキュリティーの問題を起こしたりすることがないよう、 最新の注意が必要となります。

[332] フォント依存符号化の変換器のように、対象外となる部分は無変換とすることがあります。


[333] ASCII8ビット符号で構成される文字コードであることを前提に、 ASCII文字列なら文字コードの変換の処理を省略し、 そうでないなら文字コードの変換の処理を実行するような性能向上のための最適化が行われることがあります。

[334] こうした最適化は、例えば多バイト符号の第2バイトに ASCII文字と同じバイトが含まれる場合や、 ISO-2022-JP のような7ビット符号など、設計者の想定不足による不具合を起こすことがありますから、 十分に注意が必要です。

変換不能な入力への対処

[118] 入力となる文字コード文字列であるはずのものには、 適切に変換できないデータが含まれていることがあります。

[119] 具体的には

といったものが考えられます。

[123] 歴史的に文字コード関連規格は不正な入力への対処を明確に規定して来ませんでした。 こうしたものに遭遇したときの仕様上の「正しい」挙動が存在しないことが多いです。

[124] 近年の Encoding Standard はあらゆる入力に対する挙動を定めていますが、 例外的な存在です。

[125] こうしたデータに対する処理は実装によって様々です。

  • [126] 動作オプションを指定できるもの
  • [127] 置換文字等に置き換えるもの
  • [225] 画像その他の文字ではないオブジェクトや文字文字でないオブジェクトの組合せや文字と書式その他の付加情報の組合せに置き換えるもの
  • [128] escape 類に置き換えるもの
  • [134] 関連する他の文字コードとみなして処理するもの (>>122)
  • [143] 似た文字に置き換えるもの (>>81)
  • [129] エラーとして停止するもの
  • [130] 入力をそのまま出力するもの
  • [131] 何も出力しないもの
  • [132] 異常停止するもの、異常動作するもの

[133] このうち >>130 >>131 >>132セキュリティー上の問題を起こしかねないですし、 >>129ライブラリー等で応用の開発者が意図していない場合には好ましからざる挙動となることがあります。 不適切なデータに対する挙動はあまり意識されないことが多いようで、 このような問題を孕んだ実装は意外と多いです。

[136] ただし出力先が HTMLRTF などでフォント指定を伴う場合のように、 >>130 が正当な選択肢となることもあります。

[203] エディター類でファイルを開く場合のように、不正な入力も生のバイトとして他の文字とは区別してそのまま保持できることが好ましい用途もあります。

[179] 置換文字escape に置き換える手法は非常によく用いられますが、 通常の入力からの変換結果との区別に注意が必要となることがあります。 例えば不正な入力を ? に置き換えると、 本来の ? と区別が付かなくなり、問題となる場合もあります。

[180] 多数の文字コードの出力に対応する実装や外部から動作オプションで指定できる実装は、 置換文字escape が出力先の文字コードに存在しない文字を含む場合の対処にも注意が必要です。

[293] 変換表の類は、明示的な変換規則がない場合に使われる置換文字を記述できるようにしているものも多いです。

[294] 変換表や実装などが採用している置換文字の例:

往復変換

[306] 変換直後に逆変換することを合わせて往復変換 (roundtrip conversion) といいます。

[307] 文字コードの変換に限った用語ではありませんが、文字コードについて使うことがかなり多いようです。

[308] 往復変換文字コードの性質や文字コードの変換の性質の評価において重要な観点です。

[319] 往復変換の成否は個別の文字列についていう事もあれば、 文字コード体系全体を総括的にいうこともあります。


[309] Unicode は欧米の主要な文字コード文字をすべて収録しているため、 そのような従来文字コードから Unicode に変換し、 従来文字コードに戻すような往復変換では情報の損失がほとんど発生しません。

[310] 20世紀末から21世紀初頭のプラットフォームは保存データが従来文字コード、 内部処理用が Unicode という方式のものが多かったので、 往復変換の安全性が重要でした。

[311] 往復変換の安全性は、逆転させると違うことがあります。

[312] >>309 の場合でも、 Unicode から従来文字コードに変換し、 更に Unicode に変換すると、情報の損失が発生することがあります。

[313] アジアの多くの文字コード>>309 の性質が成り立たないか、 成り立つか自明ではありません。

[314] 個別の実装をみても、順方向と逆方向で処理が対照的になっていない事例が散見されます。 それが意図的なものかそうでないのか、 特定の実装の性質なのか文字コード自体の性質かなど、 判断するのが困難な場合が多いです。 (そういう実装は複雑な変換処理を行っているものなので、判定が難しいです。)


[315] 重複符号化往復変換が成功しなくなる大きな要因です。

[317] 文字の概念や符号化モデルが違う文字コード体系同士の変換だと往復変換が成功しないことが多くなると考えられます。

[318] 扱う文字が異なる文字コード体系だと往復変換が成功しないことが多くなります。 一方にしかない文字があると当然失敗します。


[316] 往復変換の安全性は文字レベルの情報の損失を基準に議論されることが多いようです。 バイトレベルで複数の表現があり得る文字コード体系だと往復変換によって異なるバイト列となることがあり、 それを往復変換が成功した、していないと議論されることはあまりないように思われます。


[320] 変換表の形で記述される変換は往復変換を前提にしたものが多いです。

[321] TECkit.map のように複雑めな変換を記述できる形式でも、 順方向と逆方向の変換をセットで記述し、共通部分と差分を含められるようにしている場合があります。

変換用の写像表

[35] CodepageEncoder/data/encodings at main · NielsLeenheer/CodepageEncoder · GitHub, https://github.com/NielsLeenheer/CodepageEncoder/tree/main/data/encodings

[39] Evertype: Unicode Mapping Tables, , https://www.evertype.com/standards/mappings/

[40] sourceware.org Git - glibc.git/tree - iconvdata/, https://sourceware.org/git/?p=glibc.git;a=tree;f=iconvdata;h=494f8c50b9c9662ae85bf56184db943b0526e12c;hb=b2a8d19f4adc8ca22394b02c144b4bc867adc804

[10] xorg / font / encodings · GitLab, https://gitlab.freedesktop.org/xorg/font/encodings

Unicode Consortium の変換表

[8] 当初は FTP 配布 Unicode Consortium

[20] General Questions about Chararcter Mappings, , https://web.archive.org/web/19970105193228/http://stonehand.com:80/unicode/faq/mappings/general.html

[21] Questions about Mapping Tables, , https://web.archive.org/web/19970105193219/http://stonehand.com:80/unicode/faq/mappings/tables.html

Encoding Standard の変換表

Encoding Standard

TECkit.map ファイル

.map, TECkit

文脈

[9] 文字コードの変換

研究史

[170] 個別の文字コード対の変換操作については20世紀から21世紀初頭の頃に実装戦略が研究されてきているものの、 ソースコード以外にまとまった文章の形となっていない場合が大多数と思われます。

[171] 各種の文字コード技術の変換を包括的に議論したものはほとんどないと思われます。 あるとしても欧米東アジアの主要な文字コード規格に関するものが中心で、 アジア等の各地で使われたフォント依存符号化系の変換技術や HTML, RTF の処理と組み合わさった変換技術、ワープロマクロの形の実装手法など、 本格的な研究対象として観察されてこなかったと思われる領域が多いです。

[172] 実装技法も実装そのものも Unicode への移行の進展と共に忘れられつつあり、 技術史の観点から個々の実態調査を進めて全貌を究明し、 要素技術を整理して検証していくことが急務でしょう。

関連

[7] 変形, 符号化復号, 文字コード判定, 文字コードの修復

メモ

[165] 変換器は古今東西に無数に開発され利用されてきたようです。

[166] その中には十分なテストがされておらず満足に動作しないもの、 致命的な不具合があるもの、 意図した動作なのか疑わしい挙動をするものもあります。

[167] 驚くべきことに、地域コミュニティーの中心的な Web サイトの変換器であってもそのような品質のものが珍しくありません。

[168] また、同じ文字コード体系に対応していると主張する変換器同士であっても、 互いの挙動に違いがあることは珍しくありません。 どちらが正しいか判断できないもの、どちらも違った形で壊れていると思われるものも多いです。

[169] 単純な変換表の形で表現された変換器の実装や明らかに単純な変換をしている変換器の出力なら互いに比較することもできますが、 複雑なアルゴリズムで変換しているものは、ソースコードを慎重に読み込んでも互いの挙動にどのような違いがあるのか理解しづらいことがほとんどです。