Hatena::Grouprubyist

Rubyで遊ぶよ

 | 

2009-05-10

Rubyのsqlite3-rubyは必ず文字コードASCII-8BITとして返す

04:00

Ruby 1.9 で sqlite3-ruby を使うとき、select などの返り値の文字コードは必ず ASCII-8BIT となる。

sqlite って UTF-8 しか使えないんだから UTF-8 にしちゃえばいいんじゃね? と思ったけど、試してみたら UTF-8 じゃなくても普通に通るんだね。(どこかで変なふうに覚えちゃった?)

結論から言うと、force_encoding すればいい。


#!/usr/local/ruby1.9/bin/ruby
# -*- coding: utf-8 -*-
require 'sqlite3'

encodings = ['UTF-8','EUC-JP','SHIFT_JIS']

text = "あいうえお"

db = SQLite3::Database.new('test.db')

db.execute("CREATE TABLE mytable (encode TEXT, line TEXT);")

encodings.each{|enc|
  db.execute("INSERT INTO mytable VALUES (?,?)", enc, text.encode(enc))
  result = db.get_first_row("SELECT * FROM mytable WHERE encode = ?",enc)

  p result
  puts result[1].encoding
  puts result[1].force_encoding(enc).encode('UTF-8')
}
# => 
#["UTF-8", "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"]
#ASCII-8BIT
#あいうえお
#["EUC-JP", "\xA4\xA2\xA4\xA4\xA4\xA6\xA4\xA8\xA4\xAA"]
#ASCII-8BIT
#あいうえお
#["SHIFT_JIS", "\x82\xA0\x82\xA2\x82\xA4\x82\xA6\x82\xA8"]
#ASCII-8BIT
#あいうえお

Encoding.default_external = 'UTF-8' などとしても変わらない。

うーん…

たいていの場合は文字列を生成したとき、ファイル等から読み込んだときに、エンコーディング名を指定することで、String にエンコーディングが設定されているはずですから、明示的に String#force_encoding を呼ぶ機会は滅多にないはずです。例えば、ネットワークライブラリや XML のライブラリ等が、HTTP ヘッダや XML 宣言など別のレイヤーで与えられるエンコーディングを、ライブラリ内部で設定したい場合。また、文字列として処理していた String をバイト列として処理したい場合に str.force_encoding("ASCII-8BIT") したい場合など、限られたケースになるでしょう。

ライブラリのユーザが自分で String#force_encoding を呼ばなければならないならば、多くの場合それはライブラリの設計が間違っています。このメソッドの名前が set_encoding や encoding= でなく、また破壊的メソッドしか用意されているのは、安易な利用を戒める意図もあります。

Rubyist Magazine - Ruby M17N の設計と実装

sqlite3-ruby は今までの開発者が突然手を引いたりして Ruby1.9 対応が進んでないんかな。1.2.4 が出たのが2008年8月だし。

new した直後ぐらいに指定するようにするのがいいんだろうか? 同じデータベースに違うエンコーディングの文字を入れることもできるし、厄介と言えば厄介だけど。


脱線1

sqlite-ruby や sqlite3-ruby についてはここが一番詳しい。

プロジェクトページから巡る方法が無いと思うんですけどー。


脱線2

SQLite3::Database.quote

というメソッドが用意されていた。

しかし実体は .gsub(/'/,"''") なので、前に書いたような用途の場合は自分で作らないとだめ。

saronpasusaronpasu2009/05/15 09:15本当にもったいないですよね。
実体はUTF-8なんだから、Encoding#default_internal に合わせてくれたらいいのに。

edvakfedvakf2009/09/15 03:03nokogiri のように、UTF-8 しか受け付けませんよ、というスタンスでもアリだと思います。
SHIFT_JIS 等でデータベースに入れちゃうと SQL インジェクションされたりする危険性もあるので、
安全の面でも TEXT 型なら全部 UTF-8 に変換してから格納するなどのチェックがあったほうが嬉しいですね。

取り出すときにまた Encoding.default_internal に変更するのがいいかどうかは難しいところですね。
http://gihyo.jp/admin/serial/01/charcode/0008
こういう問題もありますから。

って2009月7月に sqlite3-ruby 1.2.5 が出てますけど、この件についてはどうなんでしょうか?

edvakfedvakf2009/09/15 03:42インストールしてみましたが、この件については相変わらずでした。
考えてみれば、sqlite3-ruby では入力時のエンコードを UTF-8 に統一したとしても、
別の環境で作ったデータベースを読むこともあるでしょうから、結局 guessing が必要な場合は抜けないですね。

あとまあ sqlite3-ruby を変更しちゃうと rails 等のライブラリがどうなるかって問題もありそうです。

とりあえずこのあたりをフォーラムあたりに投げてみようかと思うのですが、どこが良いのやら。

 |