WITH-OPEN-FILE — 파일을 열고 자동으로 닫는 매크로

WITH-OPEN-FILE — 파일을 열고 자동으로 닫는 매크로

파일을 열어 그 스트림으로 작업하고, 끝나면 (정상이든 비정상이든) 자동으로 닫아주는 표준 파일 처리 매크로예요. 파일을 직접 open/close로 관리하지 않아도 되는 편리함을 줘요.

출처: CLHS: Macro WITH-OPEN-FILE

시그니처

with-open-file (stream filespec {options}*) declaration* form* => results*

본문

인자와 값 (Arguments and Values)

  • stream — 변수.
  • filespec — 경로명 지정자(pathname designator).
  • options — 폼들; 평가돼요.
  • declarationdeclare 식; 평가되지 않아요.
  • forms — 암묵적 progn.
  • resultsforms가 돌려준 값.

설명 (Description)

with-open-fileopen을 써서 filespec이 이름 짓는 파일에 대한 파일 스트림(file stream)을 만들어요. filespec은 열 파일의 이름이고, optionsopen의 키워드 인자로 쓰여요.

stream 변수가 바인딩되는 스트림 객체는 동적 지속 기간(dynamic extent)을 가지며, 그 기간은 폼에서 나가면 끝나요.

with-open-filestreamopen이 돌려준 값에 바인딩하고 forms를 암묵적 progn으로 평가해요.

throw 사용처럼 제어가 본문을 정상적으로든 비정상적으로든 떠나면 파일은 자동으로 닫혀요. 새 출력 파일을 쓰는 중에 제어가 비정상적으로 떠나면 파일은 중단(abort)되고, 가능한 한 파일이 처음부터 열린 적 없던 것처럼 파일 시스템이 남아요.

:if-exists nil이나 :if-does-not-exist nil을 쓰면 streamnil에 바인딩될 수 있어요. :if-does-not-exist nil을 쓴다면 유효한 스트림인지 확인해야 해요.

stream 변수를 할당(assign)하려고 하면 결과는 정의되지 않아요(undefined). 컴파일러는 그런 시도를 발견하면 경고를 내보낼 수 있어요.

예제 (Examples)

 (setq p (merge-pathnames "test"))
=>  #<PATHNAME :HOST NIL :DEVICE device-name :DIRECTORY directory-name
    :NAME "test" :TYPE NIL :VERSION :NEWEST>
 (with-open-file (s p :direction :output :if-exists :supersede)
    (format s "Here are a couple~%of test data lines~%")) =>  NIL
 (with-open-file (s p)
    (do ((l (read-line s) (read-line s nil 'eof)))
        ((eq l 'eof) "Reached end of file.")
     (format t "~&*** ~A~%" l)))
>>  *** Here are a couple
>>  *** of test data lines
=>  "Reached end of file."
;; Normally one would not do this intentionally because it is
;; not perspicuous, but beware when using :IF-DOES-NOT-EXIST NIL
;; that this doesn't happen to you accidentally...
 (with-open-file (foo "no-such-file" :if-does-not-exist nil)
   (read foo))
>>  hello?
=>  HELLO? ;This value was read from the terminal, not a file!

;; Here's another bug to avoid...
 (with-open-file (foo "no-such-file" :direction :output :if-does-not-exist nil)
   (format foo "Hello"))
=>  "Hello" ;FORMAT got an argument of NIL!

두 번째 칸의 예제들은 :if-does-not-exist nilfoonil에 바인딩된 상태에서 읽기/쓰기를 시도해 생기는 함정들을 보여줘요. 파일이 열리지 않았더라도 (read foo)는 터미널에서 값을 읽어버리고, (format foo ...)nil을 인자로 받아 들아 최종값을 돌려주죠.

부수 효과 (Side Effects)

진입 시 filename이 이름 짓는 파일에 대한 스트림을 만들고, 종료 시 스트림을 닫아요. 어떤 구현에서는 파일이 열려 있는 동안 잠길(lock) 수도 있어요. 스트림이 출력 스트림이면 파일이 만들어질 수 있어요.

영향 (Affected By)

호스트 컴퓨터의 파일 시스템.

특이 상황 (Exceptional Situations)

open 함수 참고.

참고 (Notes)

없음.

더 알아보기

  • open — 파일 스트림을 여는 함수
  • close — 스트림을 닫는 함수
  • pathname, logical-pathname — 경로명 다루기