Ottieni tutte le corrispondenze regexp nel buffer come elenco


18

Sul sito di scambio di code golf code oggi, ho trovato questa risposta in Clojure alla domanda "Ottieni tutti i link su una pagina web".

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

Senza la fantasia macro, è proprio questo:

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

Questo restituisce l'elenco:

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

Posso fare qualcosa di simile in Emacs Lisp?

Forse una funzione del genere (re-seq regexp (buffer-string))ritorna '(firstmatch secondmatch thirdmatch ...)?


Questo è ciò che M-x occurfa, ma cercherei più funzioni di basso livello per farlo.
wvxvw,

@wvxvw Questo è un buon punto, non ci ho nemmeno pensato occur. Dovrò guardare attraverso la sua fonte.
tata

Ho guardato dentro, e guai, quel codice fa troppo e non è facile riproporlo, per niente. Il mio prossimo candidato sarebbe s.el, ma forse c'è di più là fuori. Qui: github.com/magnars/s.el#s-match-strings-all-regex-string che ne dici di questo?
wvxvw,

Risposte:


16

Ecco come puoi farlo in base alle stringhe, come richiesto.

(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))

Non sembra del tutto completo, potresti estenderlo a una risposta pienamente funzionante?
Wasamasa,

1
Il codice era completo, ma ho anche aggiunto un esempio di utilizzo. Cos'altro ti piacerebbe vedere?
Alan Shutko,

1
Questa soluzione è troppo semplice, sfortunatamente. Prova (re-seq "^.*$" ""). Regexp valido, stringa valida, ma non termina mai.
Phil Lord,

8

Probabilmente vale la pena notare che invocare occurcon l'argomento universale provoca il popolamento del *Occur*buffer con solo corrispondenze, senza nomi di file, numeri di riga o informazioni di intestazione. Se combinato con un gruppo di acquisizione, ciò consente di estrarre qualsiasi modello desiderato.

Ad esempio, C-u M-x occurseguito da \"\(.*\)\"chiederà all'utente quale gruppo di acquisizione raccogliere (impostazione predefinita \1), quindi posiziona il contenuto di ogni stringa tra virgolette nel *Occur*buffer.


5

Ho una risposta emacs lisp a quella domanda pubblicata: /codegolf//a/44319/18848

Usando la stessa struttura (while (ricerca) (stampa)) è possibile modificarlo in una funzione per trasferire le corrispondenze in un buffer in un elenco e restituirlo in questo modo:

(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)))

Bella risposta, nota che si può decidere di sostituire match-stringcon match-string-no-propertiescosì l'evidenziazione della sintassi non è estratto. Potresti voler passare un regexp-group-indexda usare in modo da poter scegliere quale testo è memorizzato. Oltre a invertire l'ordine della ricerca (l'elenco corrente è l'ultimo al primo). Vedi questa risposta che include una versione modificata emacs.stackexchange.com/a/38752/2418
ideasman42

3

L'uso di s.elquesto sarebbe stato più breve, ma, sfortunatamente, dà troppe corrispondenze:

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

Se questo va bene (la regex per gli URL non è perfetta comunque), potrebbe essere solo più breve, e in caso contrario, non penso che potrei renderlo più breve della risposta di Alan Shutko.


2

Vorrei solo menzionare il motivo per cui penso che questo non sia implementato nel nucleo. Semplicemente per motivi di efficienza: non è necessario copiare, creare elenchi, passarli in giro e raccoglierli. Conservare invece l'intera stringa come buffer e operare con limiti di corrispondenza di numeri interi. Ecco come occurfunziona, ad esempio: corrisponde a una stringa alla volta e inserisce la corrispondenza *occur*. Non corrisponde a tutte le stringhe in una sola volta, inseriscile nell'elenco, esegui il ciclo sull'elenco per inserirle *occur*e immondizia per raccogliere l'elenco e le sue stringhe.

Proprio come se non scrivessi (do (def x 1) (def x (+ 2 x)))in Clojure, per impostazione predefinita non dovresti provare a fare in modo che Elisp si comporti come un linguaggio funzionale. Mi piacerebbe se lo fosse, ma dobbiamo fare i conti con quello che abbiamo al momento.


1

Se mi può essere concesso un plug-in, dai un'occhiata alla mia libreria "m-buffer".

(m-buffer-match buffer "foo")

Restituisce un elenco di marcatori a cui corrisponde foo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.