一条正则表达式闹的乌龙

“实战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中就是这么设置的

<code>(setq org-capture-templates
      '(("t" "Todo" entry (file+headline "~/Dropbox/gtd/inbox.org" "Tasks")
         "* TODO %U%?\n  :PROPERTIES:\n  :CREATED_AT: %U\n  :ID: %(uuidgen-4)\n  :END:")))/<code>

更具体一点,它们产生自其中的转义序列%U(详情可以参见 org-mode 的文档Template expansion[1])。

经过一番不是特别仔细的搜索后,我决定用string-match和replace-match函数来完成上述替换子串的需求。

然后便闹了两个乌龙。

string-match不支持扩展的正则语法

为了匹配形如[2020-02-15 Sat 14:19]这样的字符串,我的直觉便驱使我写出了这样的正则表达式

<code>"^\\[\\d+-\\d+-\\d+ \\w+ \\d+:\\d+\\]"/<code>

在 Node.js 或其它支持Shorthand Character Classes[2]的语言中,上面的正则表达式是可用的。

但是 Elisp 偏偏不是这样的语言!(Elisp 所支持的正则表达式语法可以参见这篇文档[3])因此,在 Elisp 中只好用下面这个正则表达式

<code>"^\\[[0-9]+-[0-9]+-[0-9]+ [A-Za-z]+ [0-9]+:[0-9]+\\]"/<code>

虽然 Elisp 不支持Shorthand Character Classes,但它确实支持`Character Classes`[4],但这样写出来的正则表达式更长了

<code>"^\\[[[:digit:]]+-[[:digit:]]+-[[:digit:]]+ [[:alpha:]]+ [[:digit:]]+:[[:digit:]]+\\]"/<code>

我猜你宁可写前一种对吧。

replace-match不返回新字符串

这货是用来修改一个 buffer 中的内容的……

一种 Workaround

最后我根据需求的实际情况,综合使用string-match、substring,以及format实现了替换子串的功能

<code>(defun lt-org--starts-with-timestamp-p (text)
  "返回T或NIL表示输入字符串是否以一个inactive timestamp开头。"
  ;; Emacs的正则表达式并不支持如\d和\w这样的类,所以要写成[0-9]和[A-Za-z]的形式
  (string-match "^\\[[0-9]+-[0-9]+-[0-9]+ [A-Za-z]+ [0-9]+:[0-9]+\\]" text))

(defun lt-org--delay-timestamp (text new-timestamp)
  "用NEW-TIMESTAMP替换TEXT中的inactive timestamp。

如果TEXT没有以inactive timestamp开头,则直接添加NEW-TIMESTAMP。"
  (format "%s%s" new-timestamp
          (if (lt-org--starts-with-timestamp-p text)
              (substring text 22)
              text)))/<code>

过了很久后,我终于发现了在 Elisp 中做字符串替换的正确做法……

正确答案

只要用replace-regexp-in-string函数就足够了

<code>(replace-regexp-in-string "^\\[[0-9]+-[0-9]+-[0-9]+ [A-Za-z]+ [0-9]+:[0-9]+\\]" "abc" "[2020-02-15 Sat 14:19]《业务逻辑需要关心同步还是异步吗?》")/<code>

结果为"abc《业务逻辑需要关心同步还是异步吗?》",替换很成功。

后记

如何在浩如烟海的知识(搜索引擎、在线文档)中找到自己需要的东西,也是一门学问啊。

如果你想要和我交流,欢迎点击阅读原文到我的博客上发表评论。

[1]

Template expansion: https://orgmode.org/manual/Template-expansion.html#Template-expansion

[2]

Shorthand Character Classes: https://www.regular-expressions.info/shorthand.html

[3]

文档: https://www.gnu.org/software/emacs/manual/html_node/emacs/Regexps.html

[4]

Character Classes: https://www.gnu.org/software/emacs/manual/html_node/elisp/Char-Classes.html#Char-Classes


分享到:


相關文章: