babelでportableな文字コード処理をするお話

  • 12
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

今日はbabelの紹介…というよりは使い方のまとめを書きます。本当はclojureのimport :asみたいにlocalなnicknameをつくることのできるライブラリないのかなーと思ってたのですがなかったので全然関係ないですがbabelについて書きます。

babelとは

http://common-lisp.net/project/babel/

babelは全てCommon Lispで書かれているエンコーディングライブラリです。ANSI Common Lispではエンコーディング関連は処理系依存にされている部分が割と多いのですがbabelを使うことでportableなコードを書くことが可能です。そういう事情で文字コードを扱うライブラリはbabelに依存してることが多いみたいでquicklispのDL数も割と多いです。

しかしbabelの使い方を調べようとしてもまともなドキュメントがほとんどないので便利そうな関数とかマクロをまとめてここに書こうかと思います。なーに、誰も読んでくれなくったって備忘録として使えばいいさ!

懸念

> (asdf:test-system :babel)
................................................................................
................................................................................
................................................................................
................................................................................
.................XX.XX.XX.XX....................................................
......................................................
The result of BABEL-TESTS::BABEL-TESTS is:

  #<test-run: 52 tests, 454 assertions, 8 failures in 0.390002 sec (8 failed assertions, 0 errors, none expected)>

For more details run it from the REPL and use the customized Slime inspector
to inspect the results (ASDF eats up the return values). Some inspector
features may only be available when using the Slime branch at
darcs get --lazy http://dwim.hu/darcs/hu.dwim.slime
but the official Slime should also work fine.

なんかテスト失敗しているけど大丈夫かなあ…

インストール

(ql:quickload :babel)

で完了。

使ってみる

;対応してる文字コード
(babel:list-character-encosings)
;=> (:GBK :CP932 :EUCJP ...)

;文字列->バイト列
(babel:string-to-octets "ほげ")
;=> #(227 129 187 227 129 146)

;文字コードを指定してみる
(babel:string-to-octets "ほげ" :eucjp)
;=> #(164 219 164 178)

;バイト列->文字列 (こっちも文字コード指定可)
(let ((bytes (make-array 4 :element-type '(unsigned-byte 8) 
                           :initial-contents (list 164 219 164 178))))
  (babel:octets-to-string bytes :encoding :eucjp))
;=> "ほげ"

;文字列のバイト数
(babel:string-size-in-octets "ほげ")
;=> 6

;バイト列の文字数
(let ((bytes (make-array 4 :element-type '(unsigned-byte 8) 
                           :initial-contents (list 164 219 164 178))))
  (babel:vector-size-in-chars bytes :encoding :eucjp))
;=> 2

基本的な関数はこれだけでした。そりゃドキュメントもないわ。
後、便利そうなマクロにwith-output-to-sequence/with-input-from-sequenceがありました(babel-streamsが必要)
with-output-to-string/with-input-from-stringのバイト列版のようなもの。

比較

flexi-streams

あれ、バイナリストリームを文字ストリームにしてくれる関数とかなかったっけ?とbabelのソース見ながら思っていたのですがそれはflexi-streamの話でした。

http://weitz.de/flexi-streams/

flexi-streamはclのstreamを拡張したgray-stream(ただしANSI規格外)をもとに作られていて、与えられたstreamをもとに新しいstreamを作ったりできます。

;バイナリストリームを開いて…
(defun show-utf8-crlf-text (path)
  (with-open-file (binary-input path
                :direction :input
                :element-type '(unsigned-byte 8))
    ;文字ストリームに変換できます
    (let ((input (flexi-streams:make-flexi-stream binary-input
                          :external-format (flexi-streams:make-external-format :utf-8 
                                                       :eol-style :crlf))))
      (loop :for l = (read-line input nil nil)
     :while l
     :do (format t "~a~%" l)))))

これは便利そうだし実際quicklispでも結構使われているようですが対応してる文字コードはbabelよりも少ないです。後、上の例を見ればわかりますが改行コードを指定することができます(babelはできない)

処理系固有の関数/マクロ

全部調べていると魔女化しそうなので参考になりそうなリンクを貼っておきます。

http://lispuser.net/commonlisp/japanese.html

比較まとめ

babelはstrignのencode/decodeに特化、flexi-streamsはstream関連を拡充したもののようです。両方からいいとこ取りができればいいんですがね…

後書き

https://github.com/cl-babel/babel

github見ればわかりますがbabelは最近あまり活発に更新されていない模様… ちょっと不安ですが多分大丈夫なんでしょう
portableなライブラリを書く必要があるなら別ですが、別に一つの処理系で動けばいいのなら処理系依存のコード書いちゃった方が楽かもしれません。

この投稿は Common Lisp Libraries Advent Calendar 201211日目の記事です。