From: "NARUSE, Yui" Date: 2008-01-23T07:41:13+09:00 Subject: [ruby-dev:33286] Re: requests to transcode 成瀬です。 U.Nakamura wrote: > | >(2) 変換できない文字の扱い > | > 現状は変換できない文字があると例外が発生しますが、そうい > | > う文字を「?」や「〓」などに置き換えて処理を続行したい場面 > | > もそれなりにあると思います。 > | > [ruby-core:15036]あたりからの話がそうだと思いますが、これ > | > はどうなりそうでしょうか? > | > | そうですね。実装はわりと簡単ですが、API を決めないと行けないです。 > | 最終的にブロックもうまく使いたいですが、これはもう少し先だと思います。 > | 現在考えているのは例えば File.fnmatch みたいなフラグを使うことです。 > | > | 例えば > | > | s.encode('EUC-JP', String::ENCODE_SUBSTITUTE_TARGET_DEFAULT) > | とか > | s.encode('Shift_JIS', 'UTF-8', String::DROP_BROKEN_BYTES) > | とか。勿論もう少し短くてもいいかと思います :-)。 > > ruby-coreにあったブロックを使う案はスマートだとは思います。 > statefulなencodingからの変換の失敗時にどうするかとかは悩まし > いですが。 stateful encoding を処理するは、ISO 2022 についてならば直前の escape sequence というか、現在の文字集合番号とバイト列があればよいかと。ここで 1 byte をブロックに渡すか、多バイト必要な場合なことを考えて複数バイト渡 すか、いっそのことバイト列すべてと現在の index を渡すかが迷いどころです かねぇ。 最後のを使って、さらにいろいろ情報を渡した場合、こんな感じでしょうか "Ruby\e$B$k$S!<9H6L\e(IYK^0\e(B".encode("UTF-8") do | from, # encoding convert from to, # encoding convert to state, # current state integer bytes, # bytes string converting idx, # current index | if iso_2022_single_byte_charset(state) "\\x%X" % bytes[idx] elsif bytes.length >= idx + 1 # 終了 else # idx を2つ進めさせる処理 "\\x%X" % [bytes[idx] << 8 + bytes[idx+1]] end end 情報を減らしてすっきりさせたい場合は "Ruby\e$B$k$S!<9H6L\e(IYK^0\e(B".encode("UTF-8") do | state, # current state integer bytes, # array of unknown byte | if iso_2022_single_byte_charset(state) "\\x%X" % bytes[0] elsif buf.length == 0 nil else "\\x%X\\x%X" % bytes end end # ブロックからの戻り値が # nil: 次のバイトを読んで bytes に追加し、ブロックを呼び直す # string: bytes を空にして次のバイトの処理を続行 復旧時に state の操作もしたいとなると、converter オブジェクトを返した方 がいいのかなぁ。 > フラグを使う例だと、変換不能な文字を任意の指定文字に置換する > ことはできないような気がしますが、例えばスレッドローカルな特 > 殊変数で指定するとかいうことを考えておられますか? エンコーディングごとにデフォルトの置換文字が決まっているので、通常はそれ を用いればいいかと。変えたい場合はブロックで。ハッシュなりで渡すという方 法もあるでしょうね。 実際のところ、あまり任意の置換文字を用いたいというニーズは多くはない気は します。 > | >(3) C API > | > String#encodeに相当するC APIが欲しいです。 > | > まあ、そのままならfuncallでもいいんですけど、できればもう > | > ちょっとC寄りの奴が欲しいですね。 > | > | funcall と C API のところはこちらの勉強不足でちょっと分かりませんが、 > | 具体的に例えばどういう風に C の関数を呼びたいですか。 > > 変換元Cバイト配列、変換元encoding、変換先encoding、変換失敗時 > 対応方法、というような引数で呼べると嬉しいです。 > 変換失敗時対応方法として、エラー、指定文字に置き換え、のいず > れかを選べ、エラー時には例外でなく戻り値で示してもらえると嬉 > しいですね。 思うに mbsrtowcs 的な API についての話だと思います。 まず引数が * 変換元エンコーディング rb_encoding *src_enc * 変換元 char **src_p * 変換元の終わり char *src_e * 変換先エンコーディング rb_encoding *dest_enc * 変換先 char **dest_p * 変換先の終わり char *dest_e * 変換オプション int opt * 状態 独自の構造体 tcstate_t *state 戻り値は size_t で、 * 0 または正の値: 成功 詳細: 元のバイト列をすべて変換し終えた。 戻り値は変換に成功した文字数。 fallback が skip や replace の場合は常にこれ。 引数の変化: src_p: src_e と等しい dest_p: dest_e と同じかそれより前。変換後のバイト列の終わり state: 初期状態 NOTE: ここで state は初期状態でなく ascii only かどうかなどを つっこんでおいてもいいかも。 * -1: 不正なバイト列 詳細: 元のエンコーディングの CES として不正なバイトが存在した。 引数の変化: src_p: 不正なバイト列の先頭 dest_p: 変換済み+1 state: 不正なバイト列を読む直前の状態 NOTE: EILSEQ も復旧するんですよね?mbrtowc や iconv はここで諦めますが。 * -1: 不完全なバイト列 詳細: 元のエンコーディングの CES として不完全なバイトが存在した。 ようは尻切れトンボ。 引数の変化: src_p: 不完全なバイト列の先頭 dest_p: 変換済み+1 state: 不完全なバイト列を読む直前の状態 NOTE: stateful encoding の場合は、shift sequence の次の文字まで 読んだところで初めて「文字」とする。 * -3: 変換不能な文字 詳細: 元のエンコーディングの CES としては妥当だが、 変換表に載っていない等の理由で変換できない。 引数の変化: src_p: 変換不能な文字を構成するバイト列の先頭 dest_p: 変換済み+1 state: 変換不能な文字を構成するバイト列を読む直前の状態 NOTE: どこまでがその「変換不能な文字」なのかが分からない。 state の方で何とかするか。 * -4: 容量オーバー 詳細: dest_p == dest_e になってしまった 引数の変化: src_p: 未変換なバイト列の先頭 dest_p: dest_e と等しいかその少し前 state: 最後に変換成功した状態 使い方は char *src_p = RSTRING_PTR(str); char *src_e = RSTRING_END(str); char **src_pp = &src_p; while (*src_pp != src_e) { tcstate_t state = 0; size_t err = 0; err = transcode_convert( enc_windows_31j, src_pp, src_e, enc_utf_8, dest_pp, dest_e, TRANSCODE_FB_ERROR, &state); if (err >= 0) break; switch(err) { case -1: *(dest_pp++) = '?'; break; case -2: static const *hex_tbl = "0123456789ABCDEF"; *(dest_pp++) = '\\'; *(dest_pp++) = 'x'; *(dest_pp++) = hex_tbl[((*src_pp)>>8)&0xF]; *(dest_pp++) = hex_tbl[((*src_pp) )&0xF]; break; case -3: // expand break; default: // reserved break; } src_pp++; } 勉強不足なせいでこれで十分かわかりませんが、たぶん大体こんな感じでしょうか。 -- NARUSE, Yui DBDB A476 FDBD 9450 02CD 0EFC BCE3 C388 472E C1EA