如题,难道这是common lisp的函数,emacs lisp 下没有吗
对啊,这个只有 Common Lisp 有,因为 emacs lisp 没有 stream 数据类型,with-open-stream
都沒有。
请问有代替品吗,类似的功能
想打開文件的話直接用find-file之類的就是
Emacs有流類型的,不過自帶只有網絡流
这个可以简单的写一个宏来模拟一下:
(cl-defmacro with-open-file ((str filename &key direction)
&body body)
(if (eq direction :output)
`(with-temp-file ,filename
(let ((,str (current-buffer)))
,@body))
`(with-temp-buffer
(let ((,str (current-buffer)))
(insert-file-contents ,filename)
,@body))))
下面是一个相对复杂的实现以及一些配套的函数:
(defvar-local current-position -1 "当前位置")
(defun cl-read-helper (sexp-content new-pos buf eof-error-p eof)
(with-current-buffer buf
(if (and (> current-position 0)
(<= current-position (point-max)))
(prog2
(goto-char current-position)
(eval sexp-content)
(setq-local current-position (eval new-pos)))
(if eof-error-p
(error "end of file...")
eof))))
(cl-defun cl-read-line (&optional (buf (current-buffer)) (eof-error-p t) eof)
(cl-read-helper '(buffer-substring-no-properties (point-at-bol) (point-at-eol))
'(1+ (point-at-eol)) buf eof-error-p eof))
(cl-defun cl-read-char (&optional (buf (current-buffer)) (eof-error-p t) eof)
(cl-read-helper '(char-after) '(1+ (point)) buf eof-error-p eof))
(cl-defun cl-write-line (string &optional (buf (current-buffer)))
(with-current-buffer buf
(goto-char current-position)
(insert (format "%s\n" string))
(setq-local current-position (point-max))))
(cl-defun cl-write-string (string &optional (buf (current-buffer)))
(with-current-buffer buf
(goto-char current-position)
(insert string)
(setq-local current-position (point-max))))
(cl-defun cl-file-position (buf &optional (position 0 position-p))
"只有一个参数 buf 时返回文件中的当前位置,
(已被读取或者写入流的元素数量,否则将流的位置设置到该描述的位置上).
position 的值为 :start :end 或者一个非负整数."
(with-current-buffer buf
(if (not position-p)
(1- current-position)
(setq-local current-position
(cond ((eq position :start) (point-min))
((eq position :end) (point-max))
(t (1+ position))))
t)))
(cl-defmacro with-open-file ((str filename &key (direction :input)
(element-type 'base-char)
(if-exists :supersede))
&body body)
"模拟 Common Lisp 版 with-open-file 的一个宏,str 中保存的是操作的 buffer."
(if (eq direction :output)
`(with-temp-file ,filename
(unwind-protect
(let ((,str (current-buffer)))
(if (eq ',element-type 'base-char)
(set-buffer-multibyte t)
(set-buffer-multibyte nil))
(when (eq ,if-exists :append)
(insert-file-contents ,filename))
(setq-local current-position (point-max))
,@body)
(setq-local current-position -1)))
`(with-temp-buffer
(unwind-protect
(let ((,str (current-buffer)))
(if (eq ',element-type 'base-char)
(set-buffer-multibyte t)
(set-buffer-multibyte nil))
(setq-local current-position (point-min))
(insert-file-contents ,filename)
,@body)
(setq-local current-position -1)))))
使用的话,比较简单,参数 in或者 out 实际上就是 buffer,换句话说像 print 这样要求第二个参数是 buffer 的就可以直接使用了,下面是一些例子:
(with-open-file (in "/tmp/hello.world"
:direction :input)
(message "%s" (cl-read-line in t :eof))
(message "%s" (cl-read-line))
(message "%s" (cl-read-line in nil :eof))
(message "%s" (cl-read-line in nil :eof))
(message "%s" (cl-read-line in nil :eof))
(message "%s" (cl-read-line in nil))
(message "%s" (cl-read-line in nil :eof))
(message "%s" in))
(with-open-file (output "/tmp/hello.world"
:direction :output)
(cl-write-line "hello, world")
(cl-write-line "look, good")
(print "(+ 1 2)" output))
:direction 的值为 :output 则打开输出文件,省略或者其他都是默认输入。
:if-exists :append 追加数据,否则覆盖
:element-type 默认字符流,否则字节流
配套函数最好都在这个宏里使用,否则行为未知。
1 个赞