(top)  (memo)  (rss)
Implementation Notes for CLISP の 30.5 Encodings の翻訳とサンプルなど.
ext:convert-string-to-bytes 文字列からバイト列への変換 ext:convert-string-from-bytes バイト列から文字列への変換
翻訳記事は CLISP のページ を参照してください.
サンプルは,以下の通りです.
(defun describe-string (string encoding)
(flet ((char-list (char)
(coerce
(ext:convert-string-to-bytes
(make-string 1 :initial-element char)
encoding)
'list)))
(loop for ch across string
for bits = (char-list ch)
do (format t "~A : ~{~2,'0X ~}: ~{~8,'0b ~}~&" ch bits bits))))
以下は SLIME によるセッションの例です.UTF-8 と EUC-JP を比較すると,違いが よくわかると思います.
CL-USER> (defun describe-string (string encoding)
(flet ((char-list (char)
(coerce
(ext:convert-string-to-bytes
(make-string 1 :initial-element char)
encoding)
'list)))
(loop for ch across string
for bits = (char-list ch)
do (format t "~A : ~{~2,'0X ~}: ~{~8,'0b ~}~&" ch bits bits))))
describe-string
CL-USER> (describe-string "日本語文字列" charset:utf-8)
日 : E6 97 A5 : 11100110 10010111 10100101
本 : E6 9C AC : 11100110 10011100 10101100
語 : E8 AA 9E : 11101000 10101010 10011110
文 : E6 96 87 : 11100110 10010110 10000111
字 : E5 AD 97 : 11100101 10101101 10010111
列 : E5 88 97 : 11100101 10001000 10010111
nil
CL-USER> (describe-string "日本語文字列" charset:euc-jp)
日 : C6 FC : 11000110 11111100
本 : CB DC : 11001011 11011100
語 : B8 EC : 10111000 11101100
文 : CA B8 : 11001010 10111000
字 : BB FA : 10111011 11111010
列 : CE F3 : 11001110 11110011
nil
CL-USER>
が,この関数に欠点を発見しました…… iso-2202-jp などエスケープシーケン スによる切り替えの入る(CLISP 用語だとステートフルな)エンコーディングだと
CL-USER> (describe-string "日本語文字列" charset:iso-2022-jp) 日 : 1B 24 42 46 7C 1B 28 42 : 00011011 00100100 01000010 01000110 01111100 \ 00011011 00101000 01000010 本 : 1B 24 42 4B 5C 1B 28 42 : 00011011 00100100 01000010 01001011 01011100 \ 00011011 00101000 01000010 語 : 1B 24 42 38 6C 1B 28 42 : 00011011 00100100 01000010 00111000 01101100 \ 00011011 00101000 01000010 文 : 1B 24 42 4A 38 1B 28 42 : 00011011 00100100 01000010 01001010 00111000 \ 00011011 00101000 01000010 字 : 1B 24 42 3B 7A 1B 28 42 : 00011011 00100100 01000010 00111011 01111010 \ 00011011 00101000 01000010 列 : 1B 24 42 4E 73 1B 28 42 : 00011011 00100100 01000010 01001110 01110011 \ 00011011 00101000 01000010 nil CL-USER>
のように,全日本語に `1B 24 42` と `1B 28 42` のエスケープシーケンスが 入ってしまって見難い!! 実際には下記のようにエンコードされます.
CL-USER> (let ((*print-base* 16))
(print (ext:convert-string-to-bytes "日本語文字列" charset:iso-2022-jp)))
#(1B 24 42 46 7C 4B 5C 38 6C 4A 38 3B 7A 4E 73 1B 28 42)
エスケープシーケンスは日本語列の前後に一回ずつなんですがねぇ.今回の describe-string は SHIFT-JIS の文字列のビットパターンの調査用のために作っ たもので,ステートフルなエンコーディングには向いてません,と言い訳して おきましょう.
これは簡単です.
(defun show-file-with-encoding (filename &optional (encoding :default))
(with-open-file (s filename :direction :input :external-format encoding)
(loop for line = (read-line s nil :eof)
until (eq line :eof)
do (write-line line))))
のように,external-format でエンコーディングを指定してやれば読み込み時に CLISP が 内部表現に変換してるので,日本語文字列なども正しく表示できます.
CL-USER> (defun show-file-with-encoding (filename &optional (encoding :default))
(with-open-file (s filename :direction :input :external-format encoding)
(loop for line = (read-line s nil :eof)
until (eq line :eof)
do (write-line line))))
SHOW-FILE-WITH-ENCODING
CL-USER> (show-file-with-encoding "sample.lisp" charset:utf-8)
(defun list-encodings ()
(let ((syms nil))
(do-external-symbols (s (find-package :charset)) (push s syms))
(setq syms (sort syms (lambda (a b) (string>= (symbol-name a) (symbol-name b)))))
(dolist (s syms)
(print s))))
(defun test-encoding (&optional (encoding charset:iso-8859-1))
"iso-8859-1"
(labels ((conv (v)
(ext:convert-string-to-bytes
(ext:convert-string-from-bytes v encoding)
encoding)))
(loop for i from 0 upto 255 always
(loop for j from 0 upto 255
always
(loop for x across (vector i j)
for y across (conv (vector i j))
do (format t "~A <=> ~A~%" (vector i j) (conv (vector i j)))
always (= x y))))))
;; (inspect-string "日本語表示するよ。utf-8 って 3 byte なんだ?\\" charset:shift-jis)
(defun inspect-string (string encoding)
"文字列 string を encoding として 16進数/ビットパターンを表示す
る (iso-2022-jp のようにエスケープシーケンスを使うものは冗長な表示
になる)"
(flet ((char-to-codes (c)
(coerce (ext:convert-string-to-bytes (make-string 1 :initial-element c) encoding) 'list)))
(write-string
(with-output-to-string (s)
(loop for c across string
for bytes = (char-to-codes c)
do (format s "~2A : ~{~2,'0X ~}: ~{~8,'0b ~}~&" c bytes bytes))))
nil))
NIL
CL-USER>
が,ファイルのエンコーディングを判定する機能が標準で備わっていないため 日本語の文字コードが混在している環境ではちょっと不便です.Perl の Encode::Guess モジュール相当の機能が欲しいところです.
convert-string-from-bytes のパラメータで :guess キーワードを追加して判 定できるようにする,といった拡張ですかね.
posted: 2006/03/09 23:35 | permanent link to this entry | Tags: LISP