@kyanny's blog

My thoughts, my life. Views/opinions are my own.

Encode では問題なく decode できるのに MySQL に INSERT しようとすると、できないデータ

Perl で Encode でどうたら、というのの続きです。前のエントリをかきながら、自分がしたいことをうまく言い表せてないな、ともやもやしていて、「こうすればよい」とアドバイスをいただいたのですが、それを試してみても意図したような結果が得られませんでした。

自分がここまで理解していることは、

  • MySQL-5.0 (4.1 以降か) ではテーブルごと、カラムごとに character_set を指定できる
  • SELECT するときに、 character_set と関連がある collation という比較条件を使って、 WHERE 句の検索条件として与えられた文字とデータベース内のデータを比較する
  • UNIQUE 制約があるカラムに INSERT するときも、この collation をもとにしてすでに同じ値をもつレコードが存在しないか比較をする
  • SELECT するときに collation を切り替えることもできる (SQL 発行ごとに指定できる) たとえば utf8 じゃなく binary で比較、という風にするとアルファベットの case を区別できる

以上が経験とちょっと調べて知ったことで、

  • UTF-8 の文字コードとして「正しい」文字コードの並びと、「意味的に正しい」文字コードの並びは、違う?

ここらへんがわかっていなくて、ごっちゃになっているのだろうか?

最近文字コードを解説した本をかって、読んでいるのですが、それによるとたとえば 0x01 が UTF-8 でアラビア数字の 1 のことだ、とかそういう決まりがあって、そういう個々の文字コードと、エディタやブラウザで開いたファイルの一部分が 黒丸で塗りつぶした白抜きの ? のように見えることには、あんまり関係がない? UTF-8 の文字表現として妥当じゃない文字コードの並びだからといって、例えば Encode モジュールが decode できるかどうか、とは関係がない話だってことだろうか。違う気がするけど、何が違うのかをうまく言えない・・・。

decode_utf8 なり decode("utf-8, data) なり、をして、たとえば UTF-8 のある一文字として存在しない文字コードの並びを decode しようとした場合に、 CHECK とやらをセットしておけばエラーにする、というのが普通な気がして、そういう挙動をさせるためのもののように思えるのだけど、これが違うということ?

適当に、 UTF-8 の文字コードのデータとして、

0xa3 0x4b 0x80 0x01 0xaf

とか文字コードが並んでいて、 "0xa3 0x4b 0x80" がある一文字をちゃんとあらわしていて、 "0x01 0xaf" がちゃんとあらわしていない、と仮に考えたとき、 "0x01 0xaf" が おかしいからエラー、となるのが普通のように思えるけど、 0x01 も 0xaf もそれぞれ UTF-8 の文字コードとして「あえりえないこともない(ただエディタで開いても文字は見えないけど、 0x&% とか、ありえない文字コードではないから)」から、エラーにならない、ということなんだろうか。

それはそれとして、文字コード、特にデータベースの内側と外側での扱いの違いには何度も何度も苦しめられているので、文字コードとは何か? をゼロから勉強しています。2進数と16進数の数え方とか、そういうレベルから(その買った本がそういうレベルから掘り下げて書いてあったので、渡りに船とばかり)。