文字コードに対する考察
(以下はMLに寄稿された井浪さんの記事の殆ど転載です)
「SJIS 0208規格内」とは?
(SJISの2バイト文字は)0x8140~0xEFFC までが規格内、0xFA40~0xFC4Fまでが規格外と扱われているように読めますが、おそらく、SJIS/シフトJIS、と言う話をするときに、大抵はWindowsの文字コードを思い浮かべていますが、厳密にはSJISが「JISのコードをシフトしたもの」と言う意味で取れば規格内というのは JIS X 0208 で定義されているものをシフトしたもの、つまり、~0xEAA4まで、となります。(0x8740~0x879C除く)残りは俗に言う「機種依存文字」なので、それがどうなるかは実装依存です。なおWindowsではその機種依存文字の部分を
0x8740~0x879C : Windows特殊文字(丸付き数字・「平成」など)
0xED40~0xEFFC : NEC選定IBM拡張文字
0xFA40~0xFC4B : IBM拡張文字
と呼びます。
文字コードの構成要素:
(1) 符号化文字集合(Coded Character Set :CCS)
(2) 文字符号化方式(Character Encoding Scheme :CES)
符号化文字集合(以下、文字集合)は、文字通り、文字の集まりに番号を振ったものです。JISにしてもUnicodeにしても、コンピュータ上で扱う文字を定義し、番号を振っています。日本語の文字集合には[JIS X 0201],[JIS X 0208],[JIS X 0212]等の規格があります。 文字符号化方式は、文字をどのようなバイト列で保存・またはストリームなどに流すか、ということを決めた方式です。日本語に対して、Shift_JIS,EUC-JP,iso-2022-jpなど、Unicodeに対して、UTF-8,UTF-16などがあります。要するにエンコーディングですが、SJISやEUCでは、このエンコードされた結果を「文字コード」と呼ぶ場合が多くて紛らわしいような気がします。JISやUnicodeの場合は、文字集合で割り当てられている値そのものが「文字コード」と呼ばれている場合が多いです。
まず、JISのコードから。JIS X 0201は、1バイトの文字集合で、制御コード、(半角の)英数記号・カナが定義されています。(「半角」かどうかは規格で決まってはいません)
JIS X 0208は漢字(というか、俗に言う全角文字)で、2バイトで表されます。ただし、2バイトとも7ビットで表記できる値で、さらに制御コードとかぶらないように各バイトは 0x21~0x7E までの値となります。
# SJISの0xFA40~0xFC4B、IBM拡張文字の部分は計算でJISのコード
# を算出すると、上位バイトが0x80以上なので、そういう意味では
# 規格外。
それに、0x21~0x7Eまでといってもすべて埋まっているわけでなく、
http://ash.jp/code/index.htm
から「JIS X 0208の文字コード表」を見ていただければ分かりますが、未定義領域がかなりあります。そして、未定義のところに勝手に文字があるものとしている場合が有ります。WindowsのSJISで、
0x8740~0x879C : Windows特殊文字
0xED40~0xEFFC : NEC選定IBM拡張文字
の部分がそれにあたります。
MAC・携帯電話・昔のワープロ専用機その他でも同様に、未定義領域に独自の文字が入っているはずです。
通常の Shift_JISは、文字集合[JIS X 0201]+[JIS X 0208] の文字のみ対象となります。WindowsのSJIS、具体的に「コードページ932」(CP932)と呼ばれるものはそうではない、規格外の文字が入っています。
では、FireBird/InterBase の「SJIS_0208」が何を指すかというと、「漢字部分は JIS X 0208にあるものしか使わない」という表明であり、機種依存文字はエラーになってもおかしくない、ということになります。
そうすると「SJIS_0208のテーブルに SJIS_0208で接続しても、機種依存文字もエラーにならない」と思うかもしれません。これは、サーバとクライアントで同じキャラセットを使う場合、文字コードの変換が不要で、変換前のチェック自体も省いているから、と思われます。
「EUCJ_0208 <-> SJIS_0208」だと、SJISの0xFA40~0xFC4B 部分だけエラーになり、その他機種依存文字がエラーにならないのは、[JIS X 0208]<->[SJIS]<->[EUC] は相互に計算で算出可能のため、計算で算出するために JIS X 0208 で7ビットの範囲内に収まっているかのみのチェックさえすれば後は計算できる、ということに起因しているかと思われます。そのため、7ビットの範囲に収まらないSJISの0xFA40~0xFC4Bだけエラーとなります。
「UNICODE_FSS <-> SJIS_0208]で接続し、文字を登録しようとする際、必ずコードの変換が必要になります。どのような変換がかかるかといえば、
http://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT
http://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT
です。FB/IBの変換部分もこれを基に作られています。
# 参考までに、WindowsのCP932は
# http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT
# です。
これの場合、必ず文字それぞれが1対1で変換されるため、機種依存文字についてはエラーになります。SJISの0xFA40~0xFC4B は insert時にエラー。これはJISの範囲内かチェックすることでエラー。
0x8740~0x879C 、0xED40~0xEFFC は、insertできて select時にエラー、に見えるかもしれませんが、insert->commitでエラーになるようです。
また、サーバ・クライアントともUNICODE_FSSで、クライアント側がプログラムで SJIS/EUC <-> UTF-8 への変換をした場合にも、別の問題があります。(Javaをやってきた人には既知の問題でしょう)
先ほどの、
http://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT
http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT
この2つで、機種依存文字以外に、Unicodeで別の文字にマッピングされる文字があります。例えば、「~」。
APIなどを使わず、言語独自のルーチンや言語非依存の変換ライブラリによっては、通常の変換が行われます。
0x8160 -> U+301C(WAVE DASH)
Win32API、Delphiの関数などを使って変換する場合、CP932の変換テーブルにそって変換されます。
0x8160 -> U+FF5E(FULLWIDTH TILDE)
逆変換も必要ですが、U+301C は、API/Delphiの関数では 0x8160に戻すことができません。「?」になります。同様にU+FF5Eを0x8160に変換できない環境もあります。プログラミング言語だけの問題でもなく、Web系システムでページをUTF-8にして、入力された文字を変換せずにUNICODE_FSSのフィールドに投入というような設計をした場合にも、同様に「~」がどちらのコードになってやってくるかが、ブラウザやOS依存となるため注意が必要となります。
MLでの井浪さんからの改訂版検証結果に対するコメントのほぼ転載です。
* * *
(1) 何のテストをしているか判りづらい
(2) テストデータの選出方法・テストデータ件数の算出根拠が不明
(2-1) [JIS X 0218]とは?
(2-2) [JIS X 0211]は必要?
(2-3) 件数
* * *
== (1) 何のテストをしているか判りづらい ==
元々、社内向け資料なので、テストデータの生成・DBアクセス・確認を行うツールが独自ツールであり、汎用として多少判りにくい面もあります。
※編集者注:これについては吉田さんの方で、自社用検証を提供して頂いているのである程度はやむをえません。
テストの目的・想定する結果(またはテストケース)・テスト環境・テスト結果・総括、などを整理しなおし理解しやすくするといいかもしれません。
あくまでも例)
[1] 目的
Firebirdに対し、日本語Windows上で文字の登録/検索をし、登録可能か・検索可能か・文字化けを起こさないか、確認して見た方が良いように思えます。
[1-1] SJIS_0208 のテーブルに対する読み書きで正常か。
[1-2] EUCJ_0208 のテーブルに対する読み書きで正常か。
[1-3] UNICODE_FSS のテーブルに対する読み書きで正常か。
[2] 環境
サーバ: (OS,FireBirdのバージョンetc...) : テーブル構成
テーブル名 : SJIS_TABLE // SJIS確認用テーブル
フィールド
: SJIS_CODE integer // クライアント側SJISのコード値
: SJIS_CHAR CHAR(1) CHARACTER SET SJIS_0208 // 文字
テーブル名: EUC_TABLE // EUC確認用テーブル
フィールド
: SJIS_CODE integer // クライアントSJISのコード値
: EUC_CHAR CHAR(1) CHARACTER SET EUCJ_0208 // 文字
(略)
クライアント
:(OS,FireBirdのバージョンetc...)
:(プログラミング言語、接続形態etc...)
[3] テストケース・想定する結果
SJISの範囲にある文字のコード(第1バイト 0x81-0x9F + 0xE0-0xEF、
第2バイト 0x40-0x7E + 0x80-0xFC) を、クライアント側 SJIS_0208
固定、サーバ側は SJIS_0208/EUCJ_0208/UNICODE_FSSのそれぞれの
テーブルに投入し、(以下略)
(+ テストプログラム抜粋?)
(+ 現状の表を整理したもの)
(あくまで例のため略)
* * *
=== (2) テストデータの選出方法・テストデータ件数の算出根拠が不明 ===
表の内容が明らかに変なので改善の余地があります。
・JIS X 0201 が「カナ」も入っているのに、0x20-0x7E までの95文字しか対象にしていない。0xA1~0xDFは?
・JIS X 0208 の対象の文字は、
数字・記号・かな(01~08区) : 524
第1水準 (16~47区) : 2965
第2水準 (48~84区) : 3390
文字あります。(必要であれば数えてください)
現状の表では漢字(第1+第2)が 6392文字ですが実際は6355文字、範囲は 0x889F-0x9872 , 0x989F-0xEAA4 です。文字がない部分を中途半端にカウントしてます。
・「英数記号かな」の算出もとの7073の根拠も不明です。
・「Windows特殊文字」「NEC選定IBM拡張文字」が JIS X 0208 の領域であるように見える表というのは、何か違うような気がします…
・「IBM拡張文字」が JIS X 0218 って本当?JIS X 0218は文字集合の規格ではないはずです。
・JIS X 0211がケースに入っているのはなぜでしょう?そもそも SJISとして使われていません(漢字のリードバイトとコードエリアが被る)し、まったく無意味では?
・表下部の以下の表記は変です。
|JIS X 0208 のコード範囲は以下を想定しています。
| (1Byte)0x81-0x9F / 0xE0-0xEF
| (2Byte)0x40-0x7E / 0x80-0xFC
JIS X 0208の、ではなく、SJISの2バイト文字の、では?または「JIS X 0208のコード範囲は SJISのバイトの並びで以下を想定しています」でしょうか。
また、このコード範囲でテストするなら、8836のコードがあります(未定義も含む)。それを踏まえた上で、8836のコードに対し6879文字が定義済みのため、1957のコードはエラーになる、という想定でケースを作ってあるなら話はわかりますが…。で、JIS X 0208の範囲外であるSJISのバイト列(ややこしい…)として
(1byte) 0xF0-0xFC
(2Byte) 0x40-0x7E / 0x80-0xFC
の2444文字を加え、残りを1バイト文字扱いにして表を作ります。
・テスト対象文字
1バイト文字
SJIS | 数 | |
00- 1F | 32 | 制御コード(テスト対象外) |
20- 7E | 95 | JIS X 0201英数記号 |
7F | 1 | 制御コード(テスト対象外) |
80 | 1 | 未定義 |
A0 | 1 | 未定義 |
A1- DF | 63 | JIS X 0201カナ |
FD- FF | 3 | 未定義 |
2バイト文字
SJIS | 数 | |
8140-EFFC | 8836 | JIS X 0208範囲内(非文字含) |
| (6355) | (内、文字は6355文字) |
F040-FCFC | 2444 | JIS X 0208範囲外(非文字含) |
| (388) | (内、IBM選定文字388文字) |
・想定する結果
1バイト文字
SJIS | 数 | サーバ | 想定する正常件数 |
20- 7E | 95 | SJIS | 95(100%) |
| 95 | EUCJ | 95(100%) |
| 95 | UNI | 95(100%) |
80 | 1 | SJIS | 1(100%) |
| 1 | EUCJ | 0( 0%) |
| 1 | UNI | 0( 0%) |
A0 | 1 | SJIS | 1(100%) |
| 1 | EUCJ | 0( 0%) |
| 1 | UNI | 0( 0%) |
A1- DF | 63 | SJIS | 63(100%) |
| 63 | EUCJ | 0( 0%) 2バイトになり100%になるか? |
| 63 | UNI | 63(100%) |
FD- FF | 3 | SJIS | 3(100%) |
| 3 | EUCJ | 0( 0%) |
| 3 | UNI | 0( 0%) |
2バイト文字
SJIS | 数 | サーバ | 想定する正常件数 |
8140-EFFC | 8836 | SJIS | 8836(100%) |
| 8836 | EUCJ | 8836(100%) |
| 8836 | UNI | 6879( 78%) 実際にある文字しか変換しない |
F040-FCFC | 2444 | SJIS | 2444(100%) |
| 2444 | EUCJ | 0( 0%) |
| 2444 | UNI | 0( 0%) |
UNICODE_FSS時の文字数の内訳は、前述した
数字・記号・かな(01~08区) : 524
第1水準 (16~47区) : 2965
第2水準 (48~84区) : 3390
です。
* * *
ここで、
http://tech.firebird.gr.jp/firebird/index.php?firebird_xsite=38
の表(UNICODE検証結果内訳)と比較すると、
・漢字(第1+第2) : 6354 : 6355文字あるので1文字不足です。(Notesに書いてある 0x9873-0x989E, 0xEAA5-0xEAAFは、文字コードの表では未定義です)
・英数記号かな : 525 : 524文字しかないはず…(謎)
この1文字ずつが怪しいのみで、結果としてはほぼ想定どおりとなります。