以列表形式获取缓冲区中的所有正则表达式匹配项


18

今天,在Code Golf Stack Exchange网站上,我在Clojure中找到了“获取网页上的所有链接”问题的答案

(->> (slurp "http://www.stroustrup.com")
     (re-seq #"(?:http://)?www(?:[./#\+-]\w*)+"))

没有花哨的宏,就是这样:

(re-seq #"(?:http://)?www(?:[./#\+-]\w*)+" (slurp "http://www.stroustrup.com"))

这将返回列表:

("http://www.morganstanley.com/" "http://www.cs.columbia.edu/" "http://www.cse.tamu.edu" ...)

我可以在Emacs Lisp中做类似的事情吗?

也许像(re-seq regexp (buffer-string))这样的函数返回'(firstmatch secondmatch thirdmatch ...)


这是做什么的M-x occur,但是我希望在内部寻找更多的低级功能来做到这一点。
wvxvw 2015年

@wvxvw很好,我什至没有想到occur。我将不得不查看其来源。
保姆

我看了看里面,糟糕的是,该代码做的太多了,重新利用它并不容易,一点也不容易。我的下一个候选人是s.el,但也许还有更多。在这里:github.com/magnars/s.el#s-match-strings-all-regex-string怎么样?
wvxvw 2015年

Answers:


16

这是根据要求根据字符串进行操作的方法。

(defun re-seq (regexp string)
  "Get a list of all regexp matches in a string"
  (save-match-data
    (let ((pos 0)
          matches)
      (while (string-match regexp string pos)
        (push (match-string 0 string) matches)
        (setq pos (match-end 0)))
      matches)))

; Sample URL
(setq urlreg "\\(?:http://\\)?www\\(?:[./#\+-]\\w*\\)+")
; Sample invocation
(re-seq urlreg (buffer-string))

看起来还不够完善,您能否将其扩展到一个可以正常工作的答案?
wasamasa 2015年

1
代码已完成,但我还添加了用法示例。您还想看些什么?
艾伦·舒特科

1
不幸的是,该解决方案太简单了。尝试(re-seq "^.*$" "")。有效的regexp,有效的字符串,但永不终止。
菲尔勋爵

8

可能值得注意的是,occur使用Universal参数调用会使它*Occur*仅使用匹配项填充缓冲区-没有文件名,行号或标题信息。与捕获组结合使用时,可以提取所需的任何模式。

例如,C-u M-x occur其后\"\(.*\)\"将提示用户收集哪个捕获组(默认\1),然后将每个带引号的字符串的内容放入*Occur*缓冲区。


5

我对发布的问题有一个emacs lisp答案:https : //codegolf.stackexchange.com/a/44319/18848

使用相同的(while(搜索)(打印))结构,您可以将其修改为一个函数,以将缓冲区中的匹配项推送到列表中,然后将其返回,如下所示:

(defun matches-in-buffer (regexp &optional buffer)
  "return a list of matches of REGEXP in BUFFER or the current buffer if not given."
  (let ((matches))
    (save-match-data
      (save-excursion
        (with-current-buffer (or buffer (current-buffer))
          (save-restriction
            (widen)
            (goto-char 1)
            (while (search-forward-regexp regexp nil t 1)
              (push (match-string 0) matches)))))
      matches)))

不错的答案,请注意,您可能希望替换为match-stringmatch-string-no-properties这样就不会提取语法高亮显示。您可能希望传递一个regexp-group-index来使用,以便选择要存储的文本。以及反转搜索顺序(当前列表为倒数第一)。请参阅此答案,其中包括修改后的版本emacs.stackexchange.com/a/38752/2418
ideaman42 '18年

3

使用s.el它本来会更短一些,但是不幸的是,它给出了太多的匹配项:

(defun all-urls-in-buffer ()
  (s-match-strings-all
   "\\(?:http://\\)?www\\(?:[./#+-]\\w*\\)+"
   (buffer-string)))

如果可以的话(URL的正则表达式也不是完美的),这可能会更短,如果不能,那么我认为我不能比Alan Shutko的回答更短。


2

让我只说一下为什么我认为这没有在核心中实现。只是出于效率方面的考虑:无需复制,创建列表,将其传递和垃圾回收。而是将整个字符串存储为缓冲区,并以整数匹配范围进行操作。occur例如,这就是 工作方式:它一次匹配一个字符串,然后将匹配项插入*occur*。它不会一次匹配所有字符串,而是将它们放入列表中,在列表上循环以插入 *occur*并垃圾回收列表及其字符串。

就像您不会用(do (def x 1) (def x (+ 2 x)))Clojure 编写一样,默认情况下,您不应该尝试让Elisp表现得像功能语言。如果可以的话,我会很喜欢的,但是我们必须根据当前的情况做出适当的选择。


1

如果可能允许我使用插头,请查看我的“ m-buffer”库。

(m-buffer-match buffer "foo")

返回与匹配的标记列表foo

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.