《elisp》 每日一练——文件的读写

《elisp》 每日一练——文件的读写

Masimaro
2026-09-03 / 0 评论 / 0 阅读 / 正在检测是否收录...

截至到上一篇博文,我们讨论了buffer、window和快捷键相对深入一点的问题。本来作为章节的结尾今天要进行回顾和综合性的练习。但是这个专题的有些内容在前面的练习中已经出现过,并且关于buffer、window 的综合练习我现在也想不出什么能减少对新内容依赖的东西了。所以这次就打破惯例,跳过练习,直接进入文件相关专题

背景知识

emacs 中文件读写最底层的函数是 insert-file-contents。它的函数签名为:

(insert-file-contents FILENAME &optional VISIT BEG END REPLACE)

它会将文件读取到的内容插入到当前 buffer中光标所在位置之后。我们可以通过指定参数 BEGEND 的值控制只读取文件的部分内容到buffer中。

其中 visit 如果为非 nil,它会将buffer 设置为未修改状态,表示你正在对文件进行访问操作。这个时候你可以认为Emacs将文件和当前的buffer做了绑定,虽然它不会主动同步buffer的内容到文件,但是二者已经建立起关联了。此时我们无法做到只拷贝部分内容到buffer了。而如果为非 nil 你可以理解为当前buffer只是文件内容的一个拷贝,它们二者没有关系。

根据官方文档的说明,visit这个特性应该由 find-file-noselect 函数使用,我们不应该自己使用它。

insert-file-contents 类似的函数还有一个 insert-file-contents-literally。insert-file-contents 有一些高级的副作用,例如它会根据文件类型进行解码或者进行解压缩。它会忠实的读取文件中的二进制数据,没有其他额外多余的动作。它有点像C语言中的read 函数,如果你需要处理原始的二进制数据,或需要完全控制数据解码过程,你就可以使用它来读取最原始的数据。

在它们上面比较高级一点的函数就是 find-filefind-file-noselect了。它们都是将文件读取到buffer中并建立文件和buffer 的关联。一般来讲我们打开一个文件,然后使用Emacs操作文件对应的buffer、最后写回文件,最标准的做法就是使用这两个函数了。它们的区别在于find-file 在打开并读取文件之后会将窗口跳转到新打开文件所在buffer。

将buffer 内容写入到文件中相对底层的函数是 write-region, 从名字上看出它就是将buffer中的一个区域写入到文件中。

(write-region START END FILENAME &optional APPEND VISIT LOCKNAME MUSTBENEW)

其中 STARTEND 表示buffer中的一个 regionFILENAME 是一个文件名。比较特殊的一种情况是 START 可以是一个字符串,此时 END 将被忽略,该函数会将字符串写入到文件中。

APPEND 可以是一个 t 表示将内容追加到文件尾部,或者是一个整数,表示从文件开头定位到该字节偏移处,并从那里写入数据。

VISIT 的含义与我们前面介绍的 insert-file-contentsVISIT 参数类似,也是一个用来建立联系的参数。

mustbenew 表示写入的文件是否必须是一个不存在的文件,当我们传入非 nil 时emacs 会请求确认,而传入 excl 符号时,如果存在同名文件,Emacs会直接报错。

如果我们仅仅想要追加的话,可以使用 append-to-file。另外我们使用更频繁的函数可能是 save-buffersave-some-buffers。如果我们要对当前buffer执行另存为操作的话,可以使用 write-file

另外,有一个创建临时buffer并将buffer内容保存到文件中的宏 with-temp-file。它创建一个临时buffer,并设置当前buffer为新建的临时buffer,在宏结束时会将buffer切换回来并且将临时buffer的内容保存到设置的文件。

练习

本次的练习我们来实现一个小的灵感捕获器,与org-capture 类似,但是我们没有那么多复杂的功能。它的主要功能如下:

  • 使用 C-x c 来打开一个新的buffer
  • 在新buffer中写下你的想法
  • 在新的buffer中,如果使用 C-x s 会调用我们的保存函数
  • 如果我们定义了记录灵感的文件,会进行追加,否则会提示我们输入保存灵感文件的位置
  • 记录时会先写入当前时间戳,然后将当前buffer内容追加到文件

我们实现的代码如下:

(defvar my/inspiration-file nil)

(defun my/inspiration-capture()
  (interactive)
  "捕获灵感"
  (let ((buffer (generate-new-buffer "*inspiration*")))
    (switch-to-buffer buffer)
    (goto-char (point-min))
    (keymap-local-set "C-x s" #'my/save-inspiration)))

(defun my/save-inspiration()
  "保存灵感到指定文件"
  (interactive)
  (unless my/inspiration-file
    (setq my/inspiration-file (read-file-name "请输入保存灵感的文件名称:")))
  (let ((content (buffer-string))
        (timestamp (format-time-string "[%Y-%m-%d %H:%M:%S]")))
    (write-region (concat "\n" timestamp content)
                  nil ;;START 参数为 字符串,所以这个参数被忽略
                  my/inspiration-file
                  t ;; 追加
                  nil nil nil)
    (kill-buffer (current-buffer)))) ;; 作为临时buffer,可以选择关闭

(keymap-global-set "C-x c" #'my/inspiration-capture)

小结

本篇我们介绍了关于emacs 中文件读写的操作,本文中介绍的函数如下:

  • insert-file-contents
  • insert-file-contents-literally
  • find-file-noselect
  • find-file
  • write-region
  • append-to-file
  • save-buffer
  • save-some-buffers
  • with-temp-file
0

评论 (0)

取消