截至到上一篇博文,我们讨论了buffer、window和快捷键相对深入一点的问题。本来作为章节的结尾今天要进行回顾和综合性的练习。但是这个专题的有些内容在前面的练习中已经出现过,并且关于buffer、window 的综合练习我现在也想不出什么能减少对新内容依赖的东西了。所以这次就打破惯例,跳过练习,直接进入文件相关专题
背景知识
emacs 中文件读写最底层的函数是 insert-file-contents。它的函数签名为:
(insert-file-contents FILENAME &optional VISIT BEG END REPLACE)它会将文件读取到的内容插入到当前 buffer中光标所在位置之后。我们可以通过指定参数 BEG 和 END 的值控制只读取文件的部分内容到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-file 和 find-file-noselect了。它们都是将文件读取到buffer中并建立文件和buffer 的关联。一般来讲我们打开一个文件,然后使用Emacs操作文件对应的buffer、最后写回文件,最标准的做法就是使用这两个函数了。它们的区别在于find-file 在打开并读取文件之后会将窗口跳转到新打开文件所在buffer。
将buffer 内容写入到文件中相对底层的函数是 write-region, 从名字上看出它就是将buffer中的一个区域写入到文件中。
(write-region START END FILENAME &optional APPEND VISIT LOCKNAME MUSTBENEW)其中 START 和 END 表示buffer中的一个 region。FILENAME 是一个文件名。比较特殊的一种情况是 START 可以是一个字符串,此时 END 将被忽略,该函数会将字符串写入到文件中。
APPEND 可以是一个 t 表示将内容追加到文件尾部,或者是一个整数,表示从文件开头定位到该字节偏移处,并从那里写入数据。
VISIT 的含义与我们前面介绍的 insert-file-contents 中 VISIT 参数类似,也是一个用来建立联系的参数。
mustbenew 表示写入的文件是否必须是一个不存在的文件,当我们传入非 nil 时emacs 会请求确认,而传入 excl 符号时,如果存在同名文件,Emacs会直接报错。
如果我们仅仅想要追加的话,可以使用 append-to-file。另外我们使用更频繁的函数可能是 save-buffer 和 save-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-contentsinsert-file-contents-literallyfind-file-noselectfind-filewrite-regionappend-to-filesave-buffersave-some-bufferswith-temp-file
评论 (0)