《elisp 每日一练》——文本搜索与正则表达式匹配

《elisp 每日一练》——文本搜索与正则表达式匹配

Masimaro
2026-08-07 / 0 评论 / 1 阅读 / 正在检测是否收录...

前面我们介绍的都是与光标位置有关的东西,都是建立在光标基础上的操作。很多时候我们在完成相关功能时都要借助光标的移动来完成。相信各位读者看到现在已经可以很熟练的通过光标来完成一些简单的工作了。

现在我们正式进入文本操作的范畴,文本也是一种数据,数据的操作无外乎增删改查。我们先从查开始

基础知识铺垫

Emacs中查找主要通过正则表达式。关于正则表达式的详细内容我就不介绍了,如果你写过代码应该对它的概念不陌生。而Eamcs的正则表达式虽然与主流的编程语言在规则写法上略有不同,不过现在有AI的加持,只要说出需求一般AI可以给出准确的正则表达式

Emacs 中常用的搜索函数是 search-forwardre-search-forward。它们一个是严格的字符串匹配一个是正则表达式搜索。

(search-forward STRING &optional BOUND NOERROR COUNT)
(re-search-forward REGEX &optional BOUND NOERROR COUNT)

它们的参数都是类似的:

  • STRING/REGEX 是待匹配的子串或者正则表达
  • BOUND 表示搜索的截至位置,如果传入nil表示搜索结束位置为buffer的结尾或者指定的 Narrow 结尾
  • NOERROR: 是否进行错误处理,如果为nil的话会进行错误处理,默认的情况下会直接报错崩溃,一般建议是这里设为 t
  • COUNT : 表示要跳转到后面第几次匹配的位置, 如果为负数的话会向前查找。

对于这两个函数,我们需要注意下面几点:

  • 光标移动的“副作用”,它会将光标跳到匹配文本的末尾
  • BOUND 表示的是搜索结尾的位置,那么它的开始位置是在哪呢?它的开始位置是光标当前所在区域。或者是当前Narrow区域的开始位置。
  • COUNT 为负值时光标会停留在匹配文本的开头,类似于调用 search-backwardre-earch-backward
  • search-backwardre-earch-backward 函数中如果参数为正值,会向上找并将光标放置于匹配文本的开头。为负值的行为与 search-forwardre-search-forward 相同

匹配到的文字我们可以通过 match-string 来获取。它的原形如下:

(match-string NUM &optional STRING)

理论上讲,采用精确匹配的情况下,我们已经知道了匹配得到的字符串是什么了,不需要额外的获取,但是Emacs还是在 match-string 函数中给这种情况提供了支持。NUM如果我们使用精确匹配的方式一般填写0就好了,因为0代表获取完整匹配的字符串,而填入1、2、3这样的数字用来获取正则表达式中第 N 个捕获组(括号 ( ... ) 内匹配的内容)。

STRING 是一个可选的参数,如果我们上一次匹配是从buffer中进行的,那么这次我们可以在这个位置给 nil 或者默认,如果是在一个字符串中执行了匹配,那么我们需要填写上次匹配的字符串。

这个函数还需要注意:该函数不执行匹配操作,永远只获取上一次执行匹配的结果。所以我们在执行匹配之后保存结果之前不能再次执行匹配否则之前的结果会丢失。

执行完匹配之后除了可以自己写代码进行保存之外,Emacs还提供了宏 save-match-data 用来保存之前执行匹配的结果。

既然我们上面说,match-string 可以获取上次从字符串中执行匹配操作的结果,那么怎么利用字符串执行匹配操作呢?Emacs提供了函数 string-match

(string-match REGEXP STRING &optional START)

它的函数原形就简单很多了。这里就不细说了。需要注意,Emacs并没有为精确匹配提供类似的函数。实际上通过正则表达式也可以执行精确匹配。

如果你不想获取匹配结果只想知道是否能有匹配结果可以通过函数 string-match-p,与 match-string 相比它少了将匹配结果保存的操作,效率更高一些。

匹配相关的函数我们都介绍的差不多了,接着再介绍一下匹配时与光标有关的函数。

  • match-beginning 可以获取匹配项起始位置,它接收一个整数参数,参数的含义与 match-string 中的相同
  • match-end 可以获取匹配项结尾位置的Point,它接收一个整数参数,参数的含义与 match-string 中的相同

练习

有了这些函数,我们来写一个能循环匹配的查找函数。所谓的循环匹配就是匹配到最后一个之后能跳转回来从开头位置继续匹配。并且我们希望匹配时将光标置于匹配字符串的开头位置。这里为了简单,我使用search-forward 精确匹配作为演示

(defun my/search-forward-loop(regexp)
  (interactive "sSearch regexp (loop):")
  (save-restriction
    (widen) ;;全局查找,需要取消之前设置的narrowing 限制
  (let ((start-point (point))) ;; 这里需要保留光标移动,只有在未找到时才恢复
    (when (looking-at regexp) ;; 如果光标已经在匹配位置的开头,需要移动到下一个,防止一直匹配同一个位置的尴尬情况
      (forward-char 1))
    (if (search-forward regexp nil t)
    (goto-char (match-beginning 0))
      ;; 未找到,尝试跳转开头,重新查找
      (progn
      (goto-char (point-min))
      (if (search-forward regexp nil t)
      (goto-char (match-beginning 0))
    (goto-char start-point)))))))

总结

本篇不涉及什么Emacs的处理机制,就是简单的一些函数介绍,这些函数我们在其他编程语言中或多或少都见过或者用过。所以本篇实际就是一篇无聊的摘录相关函数的东西。本次介绍的函数如下:

  • search-forward 进行向后的精确匹配
  • search-backward 进行向前的精确匹配
  • re-search-forward 进行正则表达式的向后匹配
  • re-search-forward 进行向后的正则表达式匹配
  • match-string 获取匹配结果
  • string-mach 使用字符串进行匹配
  • string-match-p 判断当前是否有匹配结果
  • match-beginning 获取匹配结果的起始位置
  • match-end 获取匹配结果的结束位置
0

评论 (0)

取消