2008년 9월 3일 수요일

common lisp 파일을 바이트 배열 꼴로 조금씩 끝까지 읽어주는 매크로.

lisp 은 역시 뭔가 좀 어렵군?
남이 만든 코드는 아주 이뻐 보이는데 내가 짜려고 하면 시궁창.
특히 매크로 만들거나 loop 를 쓰거나 하는 부분에선 손이 꼬이네?

어쩌다 보니 목적잃은 코드를 만들게 됐는데 그냥 버려두자니 좀 그래서 여기 적어둔다.
displaced array 를 배운건 큰 수확이었다.

;;;
;;; loop-with-file-contents
;;; buf : body 안에서 쓸, 파일의 일부가 담긴 배열
;;; path : 읽을 파일 경로
;;; element-type : 그냥 기본값쓰자. 박아두기 뭐해서 뽑아둔것.
;;; buffer-length : buf 크기. 한번에 얼마씩 읽을지 결정하는 값
;;; body : buf 에 담긴 내용을 알아서 써먹어라.
;;;
;;; 리턴값은 총 읽은 바이트 수
;;;
;;;
;;;
;;; 파일을 바이너리 형태로 읽어오고 싶어서 만들어봤다. 짜다 보니 아주
;;; 복잡해졌는데.. 남들은 이렇게 복잡하게 쓰지는 않을거란 생각이
;;; 드는군.
;;;
;;; 1. 어쩌다 보니 with- 라는 이름으로 했는데 짜놓고 보니 이게 안에서
;;; 루프를 도는 식이라 좀 쌩뚱맞은 이름이 되버렸네. 적절한 이름을
;;; 찾아보자... 였다가 그냥 성의없이 loop 를 앞에 붙여줬다 낄낄. with
;;; 로 시작해야 간지나는데 말이지.
;;;
;;; 2. 매크로의 body 부분에 buf 외에 buflen 까지 넘겨야 하는 상황을
;;; 피하기 위해서 buf 길이가 필요에 따라 줄어들길 바랬다. 즉 10 바이트
;;; 파일을 읽을때 버퍼길이가 4 라면 4,4,2 로 세번 바디가 실행이
;;; 되도록... 그런데 이짓을 하려고 보니 read-sequence 가 fill-pointer
;;; 를 받지를 않네. 좀더 고민을 해봐야 하나 하다가 문서에 displaced
;;; array 가 보이길래 그냥 그걸 썼다.
;;;
;;; 3. 매크로내에서 쓰이는 변수들이 매크로 외부의 심볼들과 쫑나지
;;; 않도록 gensyms 를 썼는데 이거 꽤나 성가시군.
;;;
;;;
(defmacro loop-with-file-contents ((buf path &key (element-type ''(unsigned-byte 8)) (buffer-length 8192)) &body body)
(let ((s (gensym))
(total-readed (gensym))
(readed (gensym)))
`(let ((,buf (make-array ,buffer-length :element-type ,element-type)))
(with-open-file (,s ,path :element-type ,element-type)
(loop
with ,total-readed fixnum = 0
for ,readed fixnum = (read-sequence ,buf ,s)
when (zerop ,readed) return ,total-readed
do (progn
(incf ,total-readed ,readed)
(let ((,buf (if (= ,readed ,buffer-length)
,buf
(make-array ,readed :element-type ,element-type :displaced-to ,buf))))
,@body)))))))


;;; 사용은 요렇게 하면 된다. 파일명이 od.lisp 인것은 원래 od 를 짜보려던것이라..
(loop-with-file-contents (buf "/tmp/od.lisp" :buffer-length 128)
(print buf))


추가.

;;; od 비슷하게 마저 짜봤다.
;;; format 의 ~X 에 인자를 줄때 두번째 인자가 캐릭터라고 되어있길래
;;; #\0 등을 주는 삽질을 했는데 그냥 '0 로 쿼트해서 주면 되는거였네...
;;; 너무 loop 에 의존하는 코딩을 하게 되는것 같아 찝찝하군.
(loop-with-file-contents (buf "/tmp/od.lisp" :buffer-length 16)
(loop for c fixnum across buf
do (format t " ~2,'0X" c)
finally (fresh-line)))


format 의 ~{ ~} 가 배열도 받았으면 정말 깔끔했을텐데 리스트만 받아먹더라.




댓글 없음: