“实战Elisp”系列旨在讲述我使用Elisp定制Emacs的经验,抛砖引玉,还请广大Emacs同好不吝赐教——如果真的有广大Emacs用户的话,哈哈哈。
序言
我要编写一个Elisp函数,其核心逻辑涉及到替换字符串中一个符合某种模式的子串。举个例子,字符串为"[2020-02-15 Sat 14:19]《业务逻辑需要关心同步还是异步吗?》"
,需要替换的是其中的[2020-02-15 Sat 14:19]
。
这个由日期和时间组成的前缀在每一次我按下C-c c t
的时候会自动产生,因为在org-capture-templates
中就是这么设置的
1 | (setq org-capture-templates |
更具体一点,它们产生自其中的转义序列%U
(详情可以参见org-mode的文档Template expansion)。
经过一番不是特别仔细的搜索后,我决定用string-match
和replace-match
函数来完成上述替换子串的需求。
然后便闹了两个乌龙。
string-match
不支持扩展的正则语法
为了匹配形如[2020-02-15 Sat 14:19]
这样的字符串,我的直觉便驱使我写出了这样的正则表达式
1 | "^\\[\\d+-\\d+-\\d+ \\w+ \\d+:\\d+\\]" |
在Node.js或其它支持Shorthand Character Classes的语言中,上面的正则表达式是可用的。
但是Elisp偏偏不是这样的语言!(Elisp所支持的正则表达式语法可以参见这篇文档)因此,在Elisp中只好用下面这个正则表达式
1 | "^\\[[0-9]+-[0-9]+-[0-9]+ [A-Za-z]+ [0-9]+:[0-9]+\\]" |
虽然Elisp不支持Shorthand Character Classes
,但它确实支持Character Classes
,但这样写出来的正则表达式更长了
1 | "^\\[[[:digit:]]+-[[:digit:]]+-[[:digit:]]+ [[:alpha:]]+ [[:digit:]]+:[[:digit:]]+\\]" |
我猜你宁可写前一种对吧。
replace-match
不返回新字符串
这货是用来修改一个buffer中的内容的……
一种Workaround
最后我根据需求的实际情况,综合使用string-match
、substring
,以及format
实现了替换子串的功能
1 | (defun lt-org--starts-with-timestamp-p (text) |
过了很久后,我终于发现了在Elisp中做字符串替换的正确做法……
正确答案
只要用replace-regexp-in-string
函数就足够了
1 | (replace-regexp-in-string "^\\[[0-9]+-[0-9]+-[0-9]+ [A-Za-z]+ [0-9]+:[0-9]+\\]" "abc" "[2020-02-15 Sat 14:19]《业务逻辑需要关心同步还是异步吗?》") |
结果为"abc《业务逻辑需要关心同步还是异步吗?》"
,替换很成功。
后记
如何在浩如烟海的知识(搜索引擎、在线文档)中找到自己需要的东西,也是一门学问啊。