Top  > 雑記帳  > キーワード別  > 文字コード

文字コードに関する雑記

  2002年02月01日(金)   UTF内での変換
UTF-8/16/32を相互に変換するプログラムを書いてみました。www.unicode.orgにあったharness.cを参考に 
 
UTF-32→UTF-16→UTF-8→UTF-16→UTF-32と、 
UTF-32→UTF-8→UTF-32 
 
を0x0〜0x10FFFF(ただしUTF-32の0xD800〜0xDFFFは除く)の範囲でテストしてOKであるところまでは確認しました。 
 
次はJIS圏とUnicode圏の変換ですが、これは何を基準テーブルとすればいいのかむずかしいところです。とりあえず22MほどあるUnicode 3.1.1のUnihan.txtをダウンロードしてきて中を見ているところです。 
 
あとAdobe Acrobat Readerを4.05から5.05に更新しました。4.05ではAcrobat Accessというplug-inを入れないとテキスト化できなかった(しかも日本語はダメだった)のが、5.05では(データにロックがかかっていない限り)単体でテキストに出力できるようになっていました。 
 
【今日の日経平均】 9,791 -206

  2002年01月31日(木)   サロゲート・エリアとUTF-8/UTF-32
UTF-8のしくみはだいたいわかったのですが、いざ変換プログラムを自分で書いてみようとしてまたわからなくなったことがあります。 
それは、UTF-16で 
 
上位部分:U+D800〜U+DBFF 
下位部分:U+DC00〜U+DFFF 
 
として使われるサロゲート・エリアのUTF-8/UTF-32での扱いです。 
つまり、UTF-8/UTF-32データの入力でこれらの値が入ってきたらどうしたらいいか、ということです。 
 
www.unicode.orgを見てみると、まずUTF-32での扱いは、Technical Report #19 (UTF-32)で、このサロゲートにあたる上位・下位のペアはUTF-32としては「irregular」なシーケンスである、と書いてあります。ということは単独で現われた時は「irregular」でない、ということでしょうか? 
 
また、UTF-8でのサロゲート・エリア・データについてはFQA (UTF & BOM)で触れられていますが、どうもCESU-8という古い形式として規定されているようです。「not encourage the use of CESU-8, but does recognize the existence 」ということでJIS X 0208-1997でのShift jISのような扱いでしょうか。 
 
勝手に解釈してしまうのは危険ですが、「普通に考えると」UTF-16で表現できないこれらのエリアに文字をマップすることはないような気がするので、とりあえず、UTF-8/UTF-32の入力データにU+D800〜U+DFFFが入ってきたらエラーにしようと思います。 
 
【今日の日経平均】 9,997 +78

  2002年01月30日(水)   ISO-2022
ISO-2022にはGL/GRという左脳と右脳のような分け方とG0-G3というバッファの概念があって少しややこしいですが、7bitのISO-2022-JPに限っていえばGLしか使わないのでそれほど面倒ではありません。 
ただ、これならEUCの方がシンプルでいいような感じもしますが。 
 
また、UnicodeのUTF-16のサロゲートの部分の実装が気になったので、unicode.orgにあるCの変換プログラムを見てみました。はじめは何で10bitのシフトなんてしているのかと思いましたが、シンプルな式で変換できることがわかりました。 
 
で、ついでにUTF-8の方も見てみましたが、UTF-8はUnicode 3.1になって長さや範囲の縛りをきつくしたようで、こちらは面倒そうです。まだちゃんと読んだわけではないのですが、ConvertUTF.cの中の第1バイトを見て続くバイトの長さをチェックするためのテーブル(trailingBytesForUTF8[256])の最後の方(0xF0〜0xFFにあたる部分)が 
 
3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 
 
となっています。なぜ4とか5とかあるのかもわかりませんが、今の規格だと第1バイトは0xF4までのように思えるので、 
 
3,3,3,3,3,_,_,_,4,4,4,4,5,5,5,5 
 
の_の部分は3でない方がいいような気もしますが・・・ 
 
それにしてもwww.unicode.orgのサイトは、どこに情報があるのか捜しづらくて困ります。 
 
【今日の日経平均】 9,919 -106

  2002年01月29日(火)   ユーザー定義域と自由領域
Shift JISでJISの区点番号を収めた後の余りの行の領域(0xF0〜0xFC)は「ユーザー定義域」としてJISにない字を入れたり、外字登録エリアとして使われました。 
 
またJIS X 0208-1997以前のJIS X 0208には、94*94の区点エリアの中に「自由領域」というものもありました。 
 
-------------------------------------------------------- 
JIS X 0208-1978 では、8〜15区と84〜94区 
JIS X 0208-1983 では、9〜15区と85〜94区 
JIS X 0208-1990 では、1983と同じ(?) 
 
Shift JISのコードで見ると次のようになります。 
 
8区〜15区 ・・・ 0x84xx〜0x88xx 
84区〜94区 ・・・ 0xEAxx〜0xEFxx 
-------------------------------------------------------- 
 
MSのShift JIS、つまりCP932は、このJIS区点の中の「自由領域」の部分に次の二つを割り当て、 
 
・NEC 拡張記号 83文字 (0x8740-0x879C) 
・NEC 拡張漢字 374文字 (0xED40-0xEEFC) 
 
Shift JISの「ユーザー定義域」の終りの方に 
 
・IBM 拡張漢字 388文字 (0xFA40-0xFC4B) 
 
を割り当てました。いわゆる機種依存文字というやつですね。 
でも、もともと「自由領域」だったところを後から使っちゃダメ、と言われても、どうしようもないような気がします。 
 
その一方で一般のユーザーからすれば、自分が登録した外字は汎用性がないことはわかっても、買った時からついてきた文字のうち、どれがそうしたエリアにあるものなのかなど意識しません。 
 
もと「自由領域」を使ったJIS X 0213のShift JISエンコードというものもあるようですが、普通に考えるとやっぱり無理があります。 
 
【今日の日経平均】 10,026 -194

  2002年01月28日(月)   Shift JIS
8bitの半角カナを採用したエンコードとしてはShift JISがありますが、あらためて調べてみるとなかなか面白いものがあります。 
 
■ 1バイト目はなぜ不連続か 
---------------------------------------------------- 
ASCII(0x00〜0x7F)と8bitの半角カナ(0xA1〜0xDF)を避けたということが大きな理由ですが、残りの範囲(0x80〜0xA0と0xE0〜0xFF)もフルには使わず、そこから0x80/0xA0/0xFD/0xFE/0xFFを取り除いた0x81〜0x9Fと0xE0〜0xFCになっています。 
 
この1バイト目を行番号と考えると、1行に2つの連続したJISの区を割り当てるとすると、47(=94/2)種類あればいいことになります。 
 
つまり、JISの区を表わすのには、0x81〜0x9F(31個)+ 0xE0〜0xEF(16個)で十分で、残りの0xF0〜0xFCの13個は「お釣り」です。で、この「お釣り」の部分が本来の「ユーザー定義域」です。 
 
■ 2バイト目はなぜ0x40からはじまるのか 
---------------------------------------------------- 
2バイト目を列番号と考えると、1行に2つのJISの区を収納するには188(=94*2)個の列が必要です。0x00〜0xFFの256個の中から188個を選ぶのに、Shift JISでは0x40からはじめています。 
 
この理由はASCIIの0x3F以下のコードとぶつからないようにしたためという話もありますが、よくわかりません。 
 
さらに0x40から188個連続で取られているわけではなく、ASCIIのdeleteにあたる0x7Fは、はずされています。 
 
結局、2バイト目の範囲は、「0x40〜0xFC(ただし0x7Fは除く)」という変なかたちになっています。行の終りと列の終りの番号が同じ(0xFC)なのは偶然ではなく、きっと意図されたものなのでしょう。 
 
結局、Shift JISでの漢字のコードのエリアは、行・列それぞれ1つづつの分断により、4つに分かれていることになります。 
 
【今日の日経平均】 10,220 +76

  2002年01月27日(日)   7bit縛りと半角カナ
エンコードとしてのJIS(ISO-2022-JP)は、7bit縛りの世界だと思っています。文字セットとしてのJISも区点の範囲(94)でこの7bit縛りの影響を受けています。 
 
ですからJIS X 0213で「面」という概念が加えられても、区・点が7bitに収まる「お行儀のよい」ものとして、「面」は新しいエスケープコード(ESC$(O と ESC$(P))で切り替えればいいよ(ISO-2022-JP-3)、という感じになっているように見えます。 
 
それに対して、半角カナはISO-2022-JPで拒絶されています。半角カナはShift JISで有名になった8bitタイプ(0xA1〜0xDF)以外に7bitタイプ(0x21〜0x5F)もあるのですから、その7bit版の方をエスケープコードで切り替えればいいだけなのですが。実際、それ用の「ESC$(I」もあったようですが、ISO-2022-JPで半角カナはダメということになって廃れてしまったようです。 
 
そもそも半角カナなんているのか、という話になると、じゃあ全角英数字なんているのか、ということにもなります。 
 
エスケープシーケンスでモード切替をするなら、いっそ「半角(ひら)かな」もルビ用に欲しい・・・という展開になりそうですが、このあたりは昔のハードウェアの制約ともからむので、むずかしいところです。 
ただ、どうせならどこかの時点で、半角カナを「1バイトひらがな」に差し替えてくれた方が面白かったかなという気がしないでもありません。

  2002年01月26日(土)   JISの94
文字セットやエンコードの発想の元を振り返ってみます。まず、JISとUnicodeで一つの文字を文字セットの中でどう表わすかというと、p=プレーンあるいは面、rr=行、cc=列とすると 
 
・JIS ・・・ p-rr-cc (p,rr,ccは10進数) 
・Unicode ・・・ U+pprrcc (p,rr,ccは16進数) 
 
ということで似たような感じです。ただUnicodeが、rr * cc= 0xFF * 0xFFであるのに対し、JISでは、rr * cc = 94 * 94です。そこで、 
 
■ なぜJISは区と点が94なのか 
------------------------------------------------------ 
これは文字セットの話とエンコードの話が混ざり合っています。 
7bit ASCIIでは0x00〜0x7F(128個)であったので、まずその範囲を守ろうという発想からきているようです。で、どこを文字範囲とするかというと0x20(space)より下は制御コードであるので対象外とし、さらに0x20(space)と最後の0x7F(delete)をはずすことにより、0x21〜0x7Eの94個が使用されることになったということです。 
つまり、0x21〜0x7Eというエンコードが先にあって、それが1〜94という区点番号になったと考えてもよさそうです。JISコード=区点番号+0x20という関係です。 
ここで興味深いのは、はじめから「かぶった」領域が使われていることです。ASCIIは0x00〜0x7Fで、JISは0x21〜0x7Eのペアで表わされるので、例えば単独で0x21と出てきた場合、それがASCIIの'!'なのか、JISの0x21nnで表わされる文字の先頭バイトなのかわかりません。 
よって、ASCIIと混在して使われるならば、どっちのモードにいるかというモード情報が必要です。それがエスケープシーケンスで実装されています。 
文字セットのレベルで言えば、JISの一つの文字は区点番号でもちろんユニークに特定できます。けれどその区点の範囲をどれだけの大きさにするか、ということがエンコードという実装側から縛られているように見えるのがおもしろいと思います。そしてその実装が、実際には混ぜて使われるということがわかりきっているASCIIと「わざと」かぶらされている点も時代の縛りと言えるでしょう。 
------------------------------------------------------ 
 
本日の自転車の走行距離は6kmでした。明日も天気が悪そうです。

  2002年01月25日(金)   TLanguages
C++のロケールで文字コード変換はcodecvtファセットが行なうようになっているのはわかりましたが、その先がまだよくわかりません。 
 
例えばwcoutにwchar_tでUnicodeの文字列を送るとShift JISで出てきますが、その逆でShift JISを送ってUnicodeを出すにはどうしたらよいか、などです。 
 
また、localeに指定する日本(語)を表わす識別文字列は、W2kでは「ja-JP」ではなく、C++ BuilderのTLanguagesクラスを使って調べてみると「JPN」でした。 
 
【今日の日経平均】 10,144 +70

  2002年01月24日(木)   ロケール
C++ Builder 5のヘルプを見ていたらロケールに関しての説明が書いてありました。C++としてのロケール・オブジェクトというものがあるのですね。locale("C")の「C」はClassicの「C」ですか。 
 
ということはC++の流儀でいうと、ストリームで文字コード変換をする場合、ctypeのファセットを細工して行なうということでしょうか。で、Windowsの場合、もし必要ならばその中でMultiByteToWideCharなどのAPIを使い、それがWindowsのコードページを参照するということになるのかな? まだヘルプを全部読んでいないので曖昧です。 
 
頭を整理しないと、言語と環境がごっちゃになりそうです。 
 
【今日の日経平均】 10,074 +33

  2002年01月23日(水)   コードページ
DOSの時代からあるコードーページという概念の位置付けがよくわからなくなってきました。どうもCP932というのはShift JISを表わすようです。W2kのコマンドプロンプトでchcpと打つと「932」と返ってきます。ロケールとコードページの関係もあやふやです。ロケールJAPANのデフォルトのコードページが932ということなのか、ロケールとコードページは切り離して考えるべきなのかよくわかりません。 
 
また、内部ではUnicodeだけれどコードページは932というのもややこしいような気もします。ベースと環境ということでしょうか。 
この932というコードページを見てシステムはShift JISをUnicodeに変換したり、その逆を行なうみたいです。system32フォルダの中にはc_nnnn.nlsというファイルが複数ありますが、これを使ってコード変換をしているのでしょう。 
 
MultiByteToWideChar()やWideCharToMultiByte()の引数にはコードページを指定するところがありますね。 
つまりJIS X 0201とJIS X 0208という文字セットをエンコードしたShift JISとUnicodeのUTF-16(サロゲートなし)の変換ということでしょうか。さらに一部、MS独特のアサインとなる部分もあるそうで、話がややこしくなります。 
 
要は、Shift JISとUnicodeの変換を行なう場合MultiByteToWideChar()などのAPIを使うべきか、外付けの変換ルーチンを使うべきか、という疑問なのですが・・・ 
多分、変換した後のデータを何に使うのか、あるいはどう使うかによってケースバイケースとなるのでしょうね。 
 
【今日の日経平均】 10,040 -10


さらに過去へ >