Pobierz wszystkie dopasowania wyrażeń regularnych w buforze jako listę

18

Na stronie Code Golf Stack Exchange znalazłem dziś odpowiedź w Clojure na pytanie „Uzyskaj wszystkie linki na stronie internetowej”.

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

Bez fantazyjnego makra jest to tylko:

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

To zwraca listę:

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

Czy mogę zrobić coś podobnego w Emacs Lisp?

Być może funkcja taka (re-seq regexp (buffer-string))zwraca '(firstmatch secondmatch thirdmatch ...)?

niania
źródło
Tak właśnie M-x occurdziała, ale zajrzałbym do wnętrza, aby znaleźć więcej funkcji niskiego poziomu.
wvxvw,
@wvxvw To dobra uwaga, nawet o tym nie myślałem occur. Będę musiał przejrzeć jego źródło.
niania
Zajrzałem do środka i biada, ten kod robi za dużo i nie jest łatwo go zmienić przeznaczenie, wcale. Moim następnym kandydatem byłby s.el, ale może jest ich więcej. Tutaj: github.com/magnars/s.el#s-match-strings-all-regex-string a co z tym?
wvxvw

Odpowiedzi:

16

Oto, jak możesz to zrobić na podstawie ciągów, zgodnie z żądaniem.

(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))
Alan Shutko
źródło
To nie wygląda na kompletne, czy możesz rozwinąć to do w pełni działającej odpowiedzi?
wasamasa,
1
Kod był kompletny, ale dodałem także przykład użycia. Co jeszcze chciałbyś zobaczyć?
Alan Shutko
1
To rozwiązanie jest niestety zbyt proste. Spróbować (re-seq "^.*$" ""). Poprawne wyrażenie regularne, poprawny ciąg, ale nigdy się nie kończy.
Phil Lord
8

Prawdopodobnie warto zauważyć, że wywołanie occurargumentem uniwersalnym powoduje, że zapełnia on *Occur*bufor tylko dopasowaniami - bez nazw plików, numerów linii ani informacji nagłówka. W połączeniu z grupą przechwytującą pozwala to wyodrębnić dowolny wzór.

Na przykład, C-u M-x occurpo którym \"\(.*\)\"pojawi się monit o wybór grupy przechwytywania (ustawienie domyślne \1), a następnie umieszczenie zawartości każdego cytowanego ciągu w *Occur*buforze.

Jack Rusher
źródło
5

Mam odpowiedź emacs lisp na to pytanie: /codegolf//a/44319/18848

Korzystając z tej samej struktury (while (szukaj) (drukuj)), możesz ją zmodyfikować w funkcję, aby wypychać dopasowania z bufora do listy i zwracać w następujący sposób:

(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)))
Jordon Biondo
źródło
Nicea odpowiedź, uwaga może chcesz wymienić match-stringsię match-string-no-propertieswięc punktem kulminacyjnym składnia nie jest pobierany. Możesz przekazać regexp-group-indexdo użycia, abyś mógł wybrać, który tekst ma być przechowywany. Oprócz odwrócenia kolejności wyszukiwania (aktualna lista jest ostatnia do pierwszej). Zobacz tę odpowiedź, która obejmuje zmodyfikowaną wersję emacs.stackexchange.com/a/38752/2418
ideasman42
3

Korzystanie z s.eltego byłoby krótsze, ale niestety daje zbyt wiele dopasowań:

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

Jeśli to jest w porządku (wyrażenie regularne dla adresów URL i tak nie jest idealne), może to być po prostu krótsze, a jeśli nie, to nie sądzę, żebym mógł go skrócić niż odpowiedź Alana Shutko.

wvxvw
źródło
2

Pozwól mi tylko wspomnieć, dlaczego uważam, że nie jest to zaimplementowane w rdzeniu. Po prostu ze względu na wydajność: nie trzeba kopiować, tworzyć list, przekazywać ich i wyrzucać śmieci. Zamiast tego przechowuj cały ciąg jako bufor i działaj z granicami dopasowania liczb całkowitych. Tak occurdziała na przykład: dopasowuje jeden ciąg naraz i wstawia dopasowanie do *occur*. Nie pasuje do wszystkich ciągów naraz, umieszcza je na liście, zapętla listę, aby ją wstawić, *occur*a śmieci zbierają listę i jej ciągi.

Tak jak nie piszesz (do (def x 1) (def x (+ 2 x)))w Clojure, nie powinieneś domyślnie starać się, aby Elisp zachowywał się jak język funkcjonalny. Chciałbym, gdyby tak było, ale musimy zrobić to, co mamy w tej chwili.

abo-abo
źródło
1

Jeśli mogę otrzymać wtyczkę, spójrz na moją bibliotekę „m-buffer”.

(m-buffer-match buffer "foo")

Zwraca listę znaczników do dopasowań foo.

Phil Lord
źródło