逆引きxyzzy lisp(ファイルアクセス) - circleratio/xyzzy GitHub Wiki
xyzzy (Common Lisp)では,ファイルの入出力にストリームを使う. with-open-file マクロを使うと,open/close をまとめてやってくれるので便利.
(with-open-file (in "c:/temp/test.txt" :direction :input)
(while (let ((l (read-line in nil)))
(print l))))
以下の例は test.bin をオープンし,50バイト読み込んで内容を表示する.
(with-open-file (in "c:/temp/test.bin" :encoding :binary)
(let ((ch))
(dotimes (n 50)
(setf ch (read-char in nil))
(print (char-code ch)))))
0
1
2
...
49
=> nil
バッファに読み込んでから料理するのがよい.
(let ((f "c:/temp/test.txt"))
(if (file-exist-p f)
(find-file f)
(msgbox "~A is not found." f)))
(with-open-file (in "c:/temp/test.txt" :direction :input)
(let ((l)
(lines 0)
(fields 0))
(while (setf l (read-line in nil))
(setf lines (+ 1 lines))
(setf fields (+ (length (split-string l #\,)) fields)))
(values lines fields)))
指定したファイル(filename) の n 行目を取り出す.
(defun pickup-line (filename n)
(with-open-file (in filename :direction :input)
(dotimes (x (- n 1))
(read-line in nil))
(read-line in nil)))
(pickup-line "c:/temp/test.txt" 5)
=> "eee"
--- test.txt の内容 ---
aaa
bbb
ccc
ddd
eee
fff
make-temp-file-name を使う. 関数が呼ばれた時点で空のファイルが作られるので注意.
ファイルが作られる場所やファイル名をオプションで指示することもできる.
(make-temp-file-name)
=> "C:/DOCUME~1/tf/LOCALS~1/Temp/~xyzib1p.tmp"
(make-temp-file-name "test" "log" "c:/temp")
=> "c:/temp/testib2c.log"
TODO
ファイルの読み込みと同様に,with-open-file を使う.
(with-open-file (out "c:/temp/test.txt" :direction :output)
(print "test" out))
出力ファイルと同名のファイルが既にある場合の処理は,if-exists を使って指示する.
(with-open-file (out "c:/temp/test.txt" :direction :output :if-exists :error)
(print "test" out))
指定できる動作は次の通り. :error: エラーメッセージを出す :append: 追記 :rename: テンポラリ名をつけて保存 :override, supercede, rename-and-delete, new-version: 上書き
(rename-file "c:/temp/test" "c:/temp/test2")
=> t
オプションは下記. *:if-exists **:error **:skip: エラーを出力しない.nilを返す. **:overwrite **:newer: ファイルが新しい場合のみ上書きする. *:if-access-denied **:error **:skip **:force: 強制的なファイル名変更を試みる.
(copy-file "C:/Temp/memo.sexp" "C:/Temp/memo.sexp.bak")
=> t
バッファの内容をフィルタリングする例については逆引きxyzzy lisp(バッファ)の項を参照.
外部ファイルを読み込んでフィルタをかける場合は下記.
(defun filter-file (filename func)
(with-open-file (in filename :direction :input)
(let ((l))
(while (setf l (read-line in nil))
(if (funcall func l)
(print l))))))
(filter-file "c:/temp/test.txt" (lambda (l) (string-match "b" l)))
=> "abc"
"bcd"
"gab"
"abc"
"bcd"
nil
--- test.txt の内容 ---
abc
bcd
cde
efg
fga
gab
abc
bcd
ANSI Common Lisp 規格では、コンス、シンボル、文字列、数値、構造体、パスネーム、配列は read/write が可能。
ハッシュについては実装依存だけど、xyzzy では保存してくれない(オブジェクトIDが書き出される…)。
(defun dump (obj)
(with-open-file (stream "c:/temp/memo.sexp" :direction :output :if-exists :supersede)
(write obj :stream stream)))
(defun restore ()
(with-open-file (stream "c:/temp/memo.sexp" :direction :input)
(read stream)))
(dump '(1 2 3 4 5))
=> (1 2 3 4 5)
(setq x (restore))
=> (1 2 3 4 5)
x
=> (1 2 3 4 5)
(with-open-file (out "c:/temp/test.bin" :direction :output :if-exist :overwrite :encoding :binary)
(dotimes (n 128)
(write-char (code-char n) out)))
=> nil
(with-open-file (in "c:/temp/test.bin" :encoding :binary)
(let ((ch))
(while
(setf ch (read-char in nil))
(print (char-code ch)))))
0
1
2
...
127
=> nil
file-executable-p は正常動作していないようだ.
(file-exist-p "C:/usr/xyzzy-0.2.2.235/xyzzy/xyzzy.wxp")
=> t
(file-directory-p "C:/usr/xyzzy-0.2.2.235/xyzzy/xyzzy.wxp")
=> nil
(file-executable-p "C:/usr/cygwin/setup.exe")
=> nil
get-file-attributes または get-file-info を使う. 後者の方が詳しい情報が得られる.
(get-file-attributes "c:/temp/testib2c.log")
=> 32
get-file-attributes で得られる属性値の意味は次の通り. :readonly: 1 :hidden: 2 :system: 4 :directory: 16 :archive: 32 :compressed: 2048
(get-file-info "c:/temp/testib2c.log")
=> (32 3506168144 0 nil)
get-file-info は「属性,最終更新時間,サイズ,短い名前」のリストを返す. 属性部分は get-file-attributes と同じ.
結果を真偽値で返す関数もある.
(file-readable-p "C:/usr/xyzzy-0.2.2.235/xyzzy/xyzzy.wxp")
=> t
(file-writable-p "C:/usr/xyzzy-0.2.2.235/xyzzy/xyzzy.wxp")
=> t
(file-length "C:/usr/xyzzy-0.2.2.235/xyzzy/xyzzy.wxp")
=> 1778315
(file-write-time "C:/usr/xyzzy-0.2.2.235/xyzzy/xyzzy.wxp")
=> 3483183828
TODO
TODO
(set-file-write-time "C:/Temp/memo.sexp" (get-universal-time))
=> t
(set-default-directory "c:/temp")
=> t
(truename "../windows")
=> "c:/Windows"
(valid-path-p "c:/temp")
(directory-namestring "c:/temp/test")
=> "c:/temp/"
実際のファイル構成は見ておらず、単にパスに含まれる最後の "/" を使って処理している。 したがって、上記の例では test がディレクトリであっても、取り除かれてしまう。 また、"c:/temp/test" が存在してもしなくても結果には影響しない。
リスト形式で取得するなら pathname-directory を使う。
(pathname-directory "C:/usr/xyzzy/xyzzy.exe")
=> ("usr" "xyzzy")
(file-namestring "C:/usr/xyzzy/xyzzy.exe")
=> "xyzzy.exe"
拡張子を除いた部分だけを取り出すなら pathname-name を使う。
(pathname-name "C:/usr/xyzzy/xyzzy.exe")
=> "xyzzy"
あまり意味はないけれど.
(defun get-directory-file (path)
(values
(directory-namestring path)
(file-namestring path)))
(get-directory-file "C:/Windows/System32/drivers/etc/hosts")
=> "C:/Windows/System32/drivers/etc/"
"hosts"
(pathname-type "C:/usr/xyzzy/xyzzy.exe")
=> "exe"
(namestring "test/test.txt")
=> "C:/usr/xyzzy-0.2.2.235/xyzzy/test/test.txt"