Obter todas as correspondências regexp no buffer como uma lista

18

Hoje, no site do Code Golf Stack Exchange, encontrei esta resposta no Clojure para a pergunta "Obter todos os links em uma página da web".

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

Sem a macro sofisticada, é apenas isso:

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

Isso retorna a lista:

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

Posso fazer algo semelhante no Emacs Lisp?

Talvez uma função como (re-seq regexp (buffer-string))essa retorne '(firstmatch secondmatch thirdmatch ...)?

babá
fonte
É M-x occurisso que acontece, mas eu procuraria dentro por mais funções de baixo nível para fazer isso.
wvxvw
@wvxvw Esse é um bom ponto, eu nem pensei occur. Vou ter que olhar através de sua fonte.
Nanny
Olhei para dentro e, ai, esse código faz muito e não é fácil adaptá-lo, de maneira alguma. Meu próximo candidato seria s.el, mas talvez haja mais por aí. Aqui: github.com/magnars/s.el#s-match-strings-all-regex-string e quanto a isso?
Wvxvw

Respostas:

16

Aqui está como você pode fazer isso com base em strings, conforme solicitado.

(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
fonte
Isso não parece completo, você poderia expandir isso para uma resposta totalmente funcional?
Wasamasa
1
O código estava completo, mas também adicionei um exemplo de uso. O que mais você gostaria de ver?
Alan Shutko
1
Esta solução é muito simples, infelizmente. Tente (re-seq "^.*$" ""). Regexp válido, sequência válida, mas nunca termina.
Phil Lord
8

Provavelmente vale a pena notar que invocar occurcom o argumento universal faz com que ele preencha o *Occur*buffer apenas com correspondências - sem nomes de arquivos, números de linhas ou informações de cabeçalho. Quando combinado com um grupo de captura, isso permite extrair qualquer padrão desejado.

Por exemplo, C-u M-x occurseguido de \"\(.*\)\"solicitará ao usuário qual grupo de captura coletar (padrão \1) e, em seguida, colocará o conteúdo de cada sequência de caracteres citada no *Occur*buffer.

Jack Rusher
fonte
5

Eu tenho uma resposta do emacs lisp para essa pergunta postada: /codegolf//a/44319/18848

Usando a mesma estrutura (while (search) (print)), você pode modificá-lo em uma função para enviar correspondências em um buffer para uma lista e retorná-lo assim:

(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
fonte
Boa resposta, observe que você pode substituir match-stringpor, match-string-no-propertiespara que o destaque da sintaxe não seja extraído. Você pode passar um regexp-group-indexpara usar para poder escolher qual texto será armazenado. Além de reverter a ordem da pesquisa (a lista atual é a penúltima). Veja esta resposta que inclui uma versão modificada emacs.stackexchange.com/a/38752/2418
ideasman42
3

Usar s.elisso teria sido mais curto, mas, infelizmente, gera muitas correspondências:

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

Se estiver tudo bem (o regex para URLs não é perfeito de qualquer maneira), isso pode ser mais curto e, se não, acho que não poderia ser mais curto do que a resposta de Alan Shutko.

wvxvw
fonte
2

Deixe-me mencionar por que acho que isso não está implementado no núcleo. Simplesmente por razões de eficiência: não há necessidade de copiar, criar listas, distribuí-las e coletá-las no lixo. Em vez disso, armazene toda a cadeia como buffer e opere com limites de correspondência inteira. É assim que occurfunciona, por exemplo: ele corresponde a uma sequência de cada vez e insere a correspondência *occur*. Ele não corresponde a todas as seqüências de uma vez, insere-as na lista, faz um loop na lista para inserir *occur*e coletar na lista e suas cadeias de lixo.

Assim como você não escreveria (do (def x 1) (def x (+ 2 x)))no Clojure, por padrão, não deveria tentar fazer com que o Elisp se comportasse como uma linguagem funcional. Eu adoraria se fosse, mas temos que fazer o devido com o que temos no momento.

abo-abo
fonte
1

Se me for permitido um plug-in, dê uma olhada na minha biblioteca "m-buffer".

(m-buffer-match buffer "foo")

Retorna uma lista de marcadores aos quais corresponde foo.

Phil Lord
fonte