読者です 読者をやめる 読者になる 読者になる

creole : 文字列/バイト列変換

common lisp library speed

四月の半ばくらいからちょこちょこ作っていたバイト列とユニコード文字列の変換ライブラリがようやく完成*1
名前: creole


まとまった情報は上記リンク先のWikiに書く予定なので、ここでは簡単な使い方と計時結果だけを載せておく。

サンプル

;;; sbcl-1.0.37
> (apropos "" :creole t)
CREOLE:*DEFAULT-EXTERNAL-FORMAT* (bound)
CREOLE:*EXTERNAL-FORMATS* (bound)
CREOLE:OCTETS-TO-STRING (fbound)
CREOLE:STRING-TO-OCTETS (fbound)

;; デフォルトのエンコーディングはUTF-8に固定
> creole:*default-external-format*
--> :UTF-8

;; 対応文字コード一覧
> creole:*external-formats*
--> ((:ASCII :US-ASCII :ANSI_X3.4-1968 :ISO-646 :ISO-646-US :|646|)
     (:CP1250 :|cp1250| :WINDOWS-1250 :|windows-1250|)
     (:CP1251 :|cp1251| :WINDOWS-1251 :|windows-1251|)
     (:CP1252 :|cp1252| :WINDOWS-1252 :|windows-1252|)
     (:CP1253 :|cp1253| :WINDOWS-1253 :|windows-1253|) (:CP1254 :|cp1254|)
     (:CP1255 :|cp1255| :WINDOWS-1255 :|windows-1255|)
     (:CP1256 :|cp1256| :WINDOWS-1256 :|windows-1256|)
     (:CP1257 :|cp1257| :WINDOWS-1257 :|windows-1257|)
     (:CP1258 :|cp1258| :WINDOWS-1258 :|windows-1258|) (:CP437 :|cp437|)
     (:CP850 :|cp850|) (:CP852 :|cp852|) (:CP855 :|cp855|) (:CP857 :|cp857|)
     (:CP860 :|cp860|) (:CP861 :|cp861|) (:CP862 :|cp862|) (:CP863 :|cp863|)
     (:CP864 :|cp864|) (:CP865 :|cp865|) (:CP866 :|cp866|) (:CP869 :|cp869|)
     (:CP874 :|cp874|) (:EBCDIC-US :CP037 :|cp037| :IBM-037 :IBM037)
     (:EUC-JP :EUCJP :|eucJP|) (:GBK :CP936)
     (:ISO-8859-10 :|iso-8859-10| :LATIN-6 :|latin-6|)
     (:ISO-8859-11 :|iso-8859-11|)
     (:ISO-8859-13 :|iso-8859-13| :LATIN-7 :|latin-7|)
     (:ISO-8859-14 :|iso-8859-14| :LATIN-8 :|latin-8|)
     (:ISO-8859-2 :|iso-8859-2| :LATIN-2 :|latin-2|)
     (:ISO-8859-3 :|iso-8859-3| :LATIN-3 :|latin-3|)
     (:ISO-8859-4 :|iso-8859-4| :LATIN-4 :|latin-4|) (:ISO-8859-5 :|iso-8859-5|)
     (:ISO-8859-6 :|iso-8859-6|) (:ISO-8859-7 :|iso-8859-7|)
     (:ISO-8859-8 :|iso-8859-8|) (:ISO-8859-9 :|iso-8859-9| :LATIN-5 :|latin-5|)
     (:KOI8-R :|koi8-r|) (:KOI8-U :|koi8-u|)
     (:LATIN-1 :LATIN1 :ISO-8859-1 :ISO8859-1)
     (:LATIN-9 :LATIN9 :ISO-8859-15 :ISO8859-15)
     (:SHIFT_JIS :SJIS :|Shift_JIS| :CP932) (:UTF-16BE :UTF16BE)
     (:UTF-16LE :UTF16LE) (:UTF-8 :UTF8) (:X-MAC-CYRILLIC :|x-mac-cyrillic|))

;; 文字列 -> バイト列
> (creole:string-to-octets "あいうえお")
--> #(227 129 130 227 129 132 227 129 134 227 129 136 227 129 138)
    T  ; 変換に成功した場合(エンコードの不能な文字列が含まれていなかった場合)はtrue

> (creole:string-to-octets "あいうえお" :external-format :euc-jp)
--> #(164 162 164 164 164 166 164 168 164 170)
    T  

;; バイト列 -> 文字列
> (creole:octets-to-string * :external-format :euc-jp)
--> "あいうえお"
    T  ; 変換に成功した場合(デコード不能なバイト列が含まれていなかった場合)はtrue

> (creole:octets-to-string **)
--> "??????????"
    NIL ; 不正なバイト列が含まれていた (変換不能部分は"?"で埋められる)

計時

例によって目安程度の計時。

参照: read-file

;;; 計時準備  
(require :creole)

;; 夏目漱石の『こころ』を使う  ※ ある程度サイズがあり、かつ日本語が多く含まれている文字列
(defvar *kokoro*  (read-file "/path/to/kokoro"))

;; 計時関数
(defun test (string external-format loop-count)
  ;; エンコード
  (format t "; エンコード: ~A~%" external-format)
  (time 
    (dotimes (i loop-count)
      (creole:string-to-octets string :external-format external-format)))

  ;; デコード
  (format t "; デコード: ~A~%" external-format)
  (time
   (let ((octets (creole:string-to-octets string :external-format external-format)))
     (dotimes (i loop-count)
       (creole:octets-to-string octets :external-format external-format))))
  'done)


;; 計時方法: sbcl, utf8の場合
> (test *kokoro* :utf8 30)
; エンコード: UTF8
Evaluation took:
  0.056 seconds of real time
  0.056004 seconds of total run time (0.056004 user, 0.000000 system)
  100.00% CPU
  176,174,868 processor cycles
  16,594,080 bytes consed
  
; デコード: UTF8
Evaluation took:
  0.087 seconds of real time
  0.088005 seconds of total run time (0.080005 user, 0.008000 system)
  [ Run times consist of 0.008 seconds GC time, and 0.081 seconds non-GC time. ]
  101.15% CPU
  274,934,237 processor cycles
  89,200,976 bytes consed
--> DONE

計時結果

処理系UTF-8: エンコード/デコードUTF-16LE: エンコード/デコードEUC-JP: エンコード/デコード
SBCL-1.0.370.056s/0.087s0.030s/0.031s0.073s/0.094s
SBCL-1.0.37(sb-ext)*20.066s/0.458s0.422s/0.652s1.478s/1.527s
CLISP-2.425.018s/10.58s2.553s/2.804s3.131s/4.562s
CLISP-2.42(ext)*30.055s/0.080s0.062s/0.069s0.678s/0.130s
ClozureCL-1.40.965s/4.215s0.872s/0.598s1.039s/1.352s
ClozureCL-1.4(ccl)*40.679s/0.555s0.549s/0.489s3.894s/2.283s
このライブラリ自体は、ユニコード(utf32)に対応しているcommon lisp処理系で動作可能なように記述されているが、見ての通りsbcl向けに(のみ)最適化されている。

*1:とはいってもまだα版。エンコード・デコード失敗時の処理方法をどうするか検討中。

*2:creole:string-to-octetsの代わりにsb-ext:string-to-octetsを用いた場合の結果。octets-to-stringも同様。

*3:creole:string-to-octetsの代わりにext:convert-string-to-bytesを、creole:octets-to-stringの代わりにext:convert-string-from-bytesを用いた場合の結果

*4:creole:string-to-octetsの代わりにccl:encode-string-to-octetsを、creole:octets-to-stringの代わりにccl:decode-string-from-octetsを用いた場合の結果