[Date Prev][Date Next][Thread Prev][][Date Index][Thread Index]

Re: decode-coding-regin() with utf-8



In article <b4m3bi4knrk.fsf@xxxxxxx>, Katsumi Yamaoka <yamaoka@xxxxxxx> writes:

>   テキストを何かの coding system でエンコードしたものを unibyte
>   なバッファに挿入してからデコードするときに、2005年3月の時点の
>   Emacs 22.0.50 では、次のような順序で行なうとうまくいかなかった
>   ようです。

>   ;; [1]
>   (decode-coding-region (point-min) (point-max) 'CODING-SYSTEM)
>   (set-buffer-multibyte t)

>   そこで、これに該当するコードを使っている emacs-w3m では、以下
>   のように順序を逆にしました。

>   ;; [2]
>   (set-buffer-multibyte t)
>   (decode-coding-region (point-min) (point-max) 'CODING-SYSTEM)

今更こんなことを言って申し訳ないのですが、
set-buffer-multibyte は非常に危い関数です。何せバッファの中の
unibyte と multibyte をごっちゃにするんですから。もしテキスト
を何かの coding system でエンコードしたものをまたデコードした
ければ
  (decode-coding-string (encode-coding-string STR CODING1) CODING2)
を使うのが安全です (Emacs 22 でも 23 でも).

> ところが、現在の Emacs 22.0.50 では [1] のやり方でも問題無いよう
> に見えます。加えて、最新の Emacs 23.0.0 では、[1] のやり方を使わ
> ないと正しくデコードできません。例えば [2] の場合、Emacs 23.0.0
> ではこんなふうになります:

> (let ((str (encode-coding-string
> 	    (string (make-char 'chinese-gb2312 86 80)
> 		    (make-char 'chinese-gb2312 57 122))
> 	    'gb2312))
>       (default-enable-multibyte-characters nil)
>       str1 str2)
>   (with-temp-buffer
>     (insert str)
>     (setq str1 (buffer-string))
>     (set-buffer-multibyte t)
>     (setq str2 (buffer-string))
>     (decode-coding-region (point-min) (point-max) 'gb2312)
>     (list str1 str2 (buffer-string))))
>  => ("\326\320\271\372"
>      "\326■\372"
>      #("\326■\301\272" 0 2 (charset chinese-gb2312)))

Emacs 23 で multibyte buffer 中の multibyte character を
decode-coding-region でどう扱おうかはまだ迷っているのですが、
基本的には decode-coding-region を multibyte-buffer(特に
unibyte-buffer を (set-buffer-multibyte t) したもの)で使うの
はお勧めできません。上記についても、どうしても既に unibyte
buffer にあるものを decode してかつその結果を multibyte
buffer にしたければ、

(let ((str (buffer-string)))
  (erase-buffer)
  (set-buffer-multibyte t)
  (insert (decode-coding-string str CODING)))

のようなことをやるのが安全です。2度も余計に string を作るの
が無駄なように思えますが、空でないバッファでの
set-buffer-multibyte はそれなりに重い処理なのでどちらが速い
かは一概には言えません。

ちなみに Emacs 23 では

(let ((str (buffer-string)))
  (erase-buffer)
  (set-buffer-multibyte t)
  (decode-coding-string str CODING (current-buffer)))

や、別のバッファを返してよいのなら

(let ((buf (generate-new-buffer "*work*")))
  (decode-coding-region (point-min) (point-max) 'iso-8859-1 buf)
  buf)

という技も使えます。

> それから、これに関連して、[2] のやり方では扱うデータによっては
> decode-coding-region の時点で Emacs 23.0.0 がクラッシュするとい
> う報告があります[3]。それを再現するコードを作ってみました。やり
> 方はともかく、こういう条件で Emacs がクラッシュしてはいけないの
> ですよね。

おっと、 testcase を有難うございます。今修正を commit しまし
た。

---
半田@AIST