Отримати всі збіги з регулярним виразом як буфер


18

Сьогодні на сайті Exchange 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))

Це виглядає не зовсім повно, чи можете ви розширити це на повністю працюючу відповідь?
wasamasa

1
Код був повним, але я також додав приклад використання. Що ще ви хотіли б побачити?
Алан Шутко

1
На жаль, це рішення є надто простим. Спробуйте (re-seq "^.*$" ""). Дійсне regexp, допустимий рядок, але він ніколи не закінчується.
Філ Лорд

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
ideaman42

3

Використання s.elцього було б коротше, але, на жаль, дає занадто багато збігів:

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

Якщо це нормально (регулярний вираз для URL-адрес все одно не ідеальний), це може бути коротше, а якщо ні, то я не думаю, що я міг би зробити його коротшим, ніж відповідь Алана Шутко.


2

Дозвольте лише зазначити, чому я думаю, що це не реалізовано в основі. Просто з міркувань ефективності: не потрібно копіювати, створювати списки, передавати їх навколо та збирати сміття. Натомість збережіть цілий рядок як буфер і оперуйте цілими межами відповідності. Так occurпрацює, наприклад: вона відповідає одному рядку за часом і вставляє відповідність у *occur*. Це не відповідає одразу всім рядкам, внесіть їх до списку, петлі у списку, які потрібно вставити, *occur*а сміття зібрати список та його рядки.

Так само, як ви б не писали (do (def x 1) (def x (+ 2 x)))в Clojure, ви не повинні за замовчуванням намагатися, щоб Елісп поводився як функціональна мова. Мені б подобалося, якби це було, але ми повинні погодитися з тим, що ми маємо на даний момент.


1

Якщо мені можуть дозволити плагін, подивіться на мою бібліотеку "m-buffer".

(m-buffer-match buffer "foo")

Повертає список маркерів до збігів foo.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.