with-open-file 怎么没这个函数啊,需要包含什么文件吗

如题,难道这是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 个赞