ClozureCLでマルチバイト文字のバイト数を判定する
以下のようなプログラムを使用してバイト数を判定していたが、これだけではやっぱり不十分だったようだ。
(defun string-byte-size (s) (let ((count 0)) (when (stringp s) (let ((strings-list (coerce s 'list))) (dolist (i strings-list) (if (typep (character i) 'standard-char) (setf count (+ count 1)) (setf count (+ count 3)))))) count))
単純に、使用するのは日本語か ASCII 文字、文字コードも UTF-8 で固定という状態だから、
- ASCII の場合は 1 バイト
- それ以外は 3 バイト(日本語は UTF-8 だと 3 バイトでカウント)
という前提で計算すればいいだろう、と思っていたが、この関数で返されるバイト数と ls -l や wc で確認できるバイト数に差が生じることがあると判明。どういう文字が差の原因になるかはわからないが、やっぱり安易すぎたか。
ClozureCL のオンラインドキュメントを確認し、使えそうな関数を確認。名前からは想像しにくいが、encode-string-to-octets で目的を達成できそうだ。以下のようにすることでバイト数が wc や ls と一致するようになった。
(defun string-byte-size (s) (if (stringp s) (multiple-value-bind (hex-array count) (encode-string-to-octets s :external-format :utf-8) count) 0))
渡された引数が文字列じゃなければ強制的に 0 にする実装だが。
真面目に実装するとすれば、
- 渡された文字列をバイトに変換
- バイトの配列になるはずなので、その配列の要素数をカウント
という感じだろうか。coerce が使えないだろうか。
? (coerce "あいうえお" 'list) (#\U+3042 #\U+3044 #\U+3046 #\U+3048 #\U+304A) ? ? (coerce "あいうえおabcdefg" 'list) (#\U+3042 #\U+3044 #\U+3046 #\U+3048 #\U+304A #\a #\b #\c #\d #\e #\f #\g) ? (length (coerce "あいうえおabcdefg" 'list)) 12 ?
完璧ではない。「あいうえお」は UTF-8 だと 15 になるはず。とりあえず、encode-string-to-octets で目的は達成できているので、真面目にやるにはどうするかはまた今度にする。それにしても、、、わかりにくいネーミングだ。multiple-value-bind を使用しないといけないのも面倒。