首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
281 阅读
2
从零开始配置 vim(15)——状态栏配置
165 阅读
3
nvim番外之将配置的插件管理器更新为lazy
156 阅读
4
2018总结与2019规划
143 阅读
5
PDF标准详解(五)——图形状态
119 阅读
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
登录
Search
标签搜索
c++
c
学习笔记
windows
emacs
文本操作术
elisp
编辑器
NeoVim
Vim
win32
读书笔记
VimScript
linux
文本编辑器
投资理财
Java
反汇编
OLEDB
数据库编程
Masimaro
累计撰写
425
篇文章
累计收到
32
条评论
首页
栏目
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2026-09-04
《elisp 每日一练》—— 低成本的文件读写
上一节我们了解了文件读写的一些api,并且通过一个保存灵感的小例子了解了Emacs中文本的操作。实际在Emacs中读写文本无外乎两种目的,第一种是用户需要编辑文本并最终保存,第二种是要读取文件内容作为参数或者内容完成后续的工作。例如利用配置文件控制Emacs的行为或者插件的行为。对于第一种我们可以使用正规的 find-file 和 find-file-noselect 将文件加载到一个buffer中,并且保存的时候使用 save-buffer 进行保存。但是这种方式太重了。Emacs在正式读写文件的时候有一些附加动作,如果我们只是简单的要里面的文本,这种开销有点太大了。今天我们主要介绍使用 with-temp-file 、insert-file-contents 、write-region 进行低成本的文件读写背景知识在上一节我们提到 visit 参数,但是我们没有详细展开,现在我们详细展开看看 find-file-noselect 在基础的读写需求上的额外操作。首先它会通过 visit 参数构建一个访问关系,它会创建一个新的缓冲区,或者复用一个已存在的缓冲区。最关键的是,它会将缓冲区与文件“关联”起来。这意味着它会设置 buffer-file-name 并记录文件的修改时间,后续Emacs就将这个buffer和对应的文件关联起来了。在读取文件的时候它还会额外进行编码转化,例如将文件由其他编码转化为utf-8,或者其他规定的编码。另外它也会进行文件格式的转化,也就是将DOS 格式的 \r\n 转化为 \n。在打开文件之后,它会根据文件的类型设置文件的Major Mode。例如python文件会设置 python-mode 在该mode下可能会进行格式化、语法高亮等等操作。在文件加载完之后,可能会调用 find-file-hook 让用户代码有机会执行额外的动作。而 save-buffer 也有一些额外的动作。首先它会检查是否真的需要保存。它会根据我们前面介绍过的 buffer 的 modified 状态来判断当前是否需要保存。接着它会进行额外的备份,例如将文件保存为 foo~.txt 这类与源文件类似的文件。在真正执行写入操作之前,它会运行 write-contents-functions write-file-functions 这类hook函数,以便用户代码在写入前有机会介入,控制写入行为。写入完成之后,它会更新buffer的状态,将它更新为未修改状态,并更新文件的上次修改时间。从上面过程看,这两个函数纯粹就是为了让用户编辑文件所设计的,它纯粹是编辑器层面的函数。如果我们只是临时的读一些像保存上次退出保存的一些状态文件,下一次启动时用于恢复,对于这类临时文件,用户甚至都不知道该文件的存在。我们如果使用 find-file-noselect 这类的函数,就有点浪费了。这种类型它们合适的方式是读取时使用 with-temp-buffer + insert-file-contents 来进行轻量级读取。如果要写入则采用 write-region。如果你需要简单的操作buffer之后将buffer内容写入,可以使用 with-temp-buffer 。你可以将 with-temp-file 看成 with-temp-buffer + insert-file-content + write-region 的组合技。练习假设我们记录了多天的工作日志。每天的日志格式为:** <2026-09-04 Fri> content content content .....我们现在提供一个函数按日志查看某天的工作日志。(defun my/check-daily-work (date) "按输入日期 DATE (格式 YYYY-MM-DD) 查看工作日志。" (interactive (list (read-string "Input date (YYYY-MM-DD): " (format-time-string "%Y-%m-%d")))) (let ((content "") (found nil) (collecting nil)) (with-temp-buffer (when (file-exists-p my/work-log-path) (insert-file-contents my/work-log-path) (let ((lines (split-string (buffer-string) "\n" t))) (while lines (let ((line (car lines))) (cond ;; 如果是日期标题行 ((string-prefix-p "** <" line) (if collecting ;; 如果正在收集,说明遇到了下一个日期,停止收集并退出循环 (setq lines nil) ;; 否则检查是否匹配目标日期 (when (string-match (concat "\\*\\* <" date) line) (setq found t) (setq collecting t) (setq content (concat line "\n"))))) ;; 如果是非标题行 (t (when collecting (setq content (concat content line "\n"))))) (setq lines (cdr lines))))))) (if (not found) (message "未找到 %s 的日志记录" date) (let ((buffer (generate-new-buffer "WorkLog"))) (switch-to-buffer buffer) (erase-buffer) (insert content)))))上述代码,我们通过 with-temp-buffer 创建一个临时的buffer,用来读取数据。处理时将所有数据按行划分,找到以 ** 开头的日期行,如果当前日期匹配上目标日期,则将 found 设置为真,后面连着的几行是我们实际得到的当前的日志内容,如果后续又找到了 ** 开头的内容表示到下一个日期了应该退出。小结本篇我们理解了find-file-noselect 在临时读取数据的弊端,并且介绍了在这种情况下的一半处理方式。本篇的api函数在上一篇已经列举过了,这次就不在说明了。
2026年09月04日
0 阅读
0 评论
0 点赞