Получить все регулярные выражения в буфере в виде списка

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
@wvxvw Это хороший момент, я даже не думал об этом occur. Мне придется просмотреть его источник.
няня
Я заглянул внутрь, и, к сожалению, этот код делает слишком много, и его совсем непросто перепрофилировать. Моим следующим кандидатом будет s.el, но, может быть, есть еще что-то там. Здесь: github.com/magnars/s.el#s-match-strings-all-regex-string как насчет этого?
wvxvw

Ответы:

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))
Алан Шутко
источник
Это выглядит не совсем полным, не могли бы вы расширить это до полностью рабочего ответа?
Васамаса
1
Код был завершен, но я также добавил пример использования. Что еще вы хотели бы увидеть?
Алан Шутко
1
Это решение слишком просто, к сожалению. Попробуй (re-seq "^.*$" ""). Допустимое регулярное выражение, допустимая строка, но она никогда не заканчивается.
Фил Лорд
8

Вероятно, стоит отметить, что вызов occurс универсальным аргументом заставляет его заполнять *Occur*буфер только совпадениями - без имен файлов, номеров строк или информации заголовка. В сочетании с группой захвата это позволяет извлекать любой шаблон по желанию.

Например, C-u M-x occurзатем следует \"\(.*\)\"запросить пользователя, для какой группы захвата собирать (по умолчанию \1), а затем поместить содержимое каждой строки в кавычках в *Occur*буфер.

Джек Рашер
источник
5

У меня есть ответ от Emacs на этот вопрос: /codegolf//a/44319/18848

Используя ту же структуру (while (search) (print)), вы можете преобразовать ее в функцию, чтобы помещать совпадения из буфера в список и возвращать ее следующим образом:

(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-stringс match-string-no-propertiesпоэтому подсветка синтаксиса не извлекается. Возможно, вы захотите передать regexp-group-indexиспользование, чтобы вы могли выбрать, какой текст будет сохранен. Так же как и обратный порядок поиска (текущий список указан первым). Посмотрите этот ответ, который включает в себя модифицированную версию emacs.stackexchange.com/a/38752/2418
ideasman42
3

Использование s.elэтого было бы короче, но, к сожалению, это дает слишком много совпадений:

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

Если это нормально (регулярное выражение для URL в любом случае не идеально), это может быть просто короче, а если нет, то я не думаю, что смогу сделать его короче, чем ответ Алана Шутко.

wvxvw
источник
2

Позвольте мне упомянуть, почему я думаю, что это не реализовано в ядре. Просто по соображениям эффективности: нет необходимости копировать, создавать списки, передавать их и собирать мусор. Вместо этого сохраните всю строку в качестве буфера и оперируйте целочисленными границами соответствия. Вот как occurработает, например: он сопоставляет одну строку за раз и вставляет совпадение в *occur*. Он не совпадает со всеми строками сразу, делает их в списке, делает цикл в списке для вставки *occur*и собирает мусор в списке и его строках.

Точно так же, как если бы вы не писали (do (def x 1) (def x (+ 2 x)))в Clojure, по умолчанию не следует пытаться заставить Elisp вести себя как функциональный язык. Я бы с удовольствием, если бы это было так, но мы должны отдать должное тому, что у нас есть на данный момент.

Або-або
источник
1

Если мне может быть разрешен плагин, взгляните на мою библиотеку "m-buffer".

(m-buffer-match buffer "foo")

Возвращает список маркеров для совпадений foo.

Фил Лорд
источник