同じストリームをバイトストリームやキャラクタストリームとして扱いたい - lisp-cookbook-ja/common-lisp GitHub Wiki
ストリームの要素を、characterでも、unsigned-byteでも扱えるようなストリームを、Bivalent Streamと呼びます。
Common LispでBivalent Streamは、 CLiki:flexi-streams を使用することによって実現可能です。
利用例
おはよう𠀋一郎!
というUTF-8のファイルが、/tmp/foo として存在するとします。
(defun utf-8-char-bytes (byte)
"最初のバイトから一文字のバイト数を算出する"
(let ((n (- 8 (integer-length (logxor byte #xff)))))
(case n
(0 1)
(1 nil)
(otherwise n))))
(defun read-utf-8-char-octets (stream)
"UTF-8の一文字分のバイトを読む"
(let* ((1stoct (read-byte stream))
(chars (utf-8-char-bytes 1stoct))
(ans (make-array chars :element-type '(unsigned-byte 8))))
(declare ((vector (unsigned-byte 8)) ans))
(setf (elt ans 0) 1stoct)
(loop :for idx :from 1 :below chars
:do (setf (elt ans idx) (read-byte stream)))
ans))
(with-open-file (in "/tmp/foo" :element-type '(unsigned-byte 8))
(let ((in (flex:make-flexi-stream in :external-format :utf-8)))
(princ
(list (read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)))
(terpri)
(file-position in 0)
(princ
(list (read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)))
nil))
;>> (#(227 129 138) は #(227 130 136) う #(240 160 128 139) 一 #(233 131 142) !)
;>> (お #(227 129 175) よ #(227 129 134) 𠀋 #(228 184 128) 郎 #(33))
;=> NIL
処理系ごとの対応状況
SBCL
SBCLでは、標準でBivalent Streamが利用可能です。利用する為には、openに:element-type :defaultを指定します。
(with-open-file (in "/tmp/foo" :element-type :default)
(princ
(list (read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)))
(terpri)
(file-position in 0)
(princ
(list (read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)
(read-char in)
(read-utf-8-char-octets in)))
nil)
;>> (#(227 129 138) は #(227 130 136) う #(240 160 128 139) 一 #(233 131 142) !)
;>> (お #(227 129 175) よ #(227 129 134) 𠀋 #(228 184 128) 郎 #(33))
;=> NIL
Allegro CL
Allegro CLは標準状態で、Bivalent Streamが利用可能になっています。