Holen Sie sich alle Regexp-Übereinstimmungen im Puffer als Liste

18

Auf der heutigen Code Golf Stack Exchange-Website habe ich diese Antwort in Clojure auf die Frage "Alle Links auf einer Webseite abrufen" gefunden.

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

Ohne das schicke Makro ist es nur so:

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

Dies gibt die Liste zurück:

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

Kann ich in Emacs Lisp etwas Ähnliches tun?

Vielleicht (re-seq regexp (buffer-string))kehrt so eine Funktion zurück '(firstmatch secondmatch thirdmatch ...)?

Kindermädchen
quelle
Dies ist, was M-x occurtut, aber ich würde nach tieferen Funktionen suchen, um das zu tun.
wvxvw
@wvxvw Das ist ein guter Punkt, über den ich nicht einmal nachgedacht habe occur. Ich muss die Quelle durchsehen.
Kindermädchen
Ich schaute hinein und oh wehe, dieser Code macht zu viel und es ist nicht einfach, ihn wiederzuverwenden, überhaupt nicht. Mein nächster Kandidat wäre s.el, aber vielleicht gibt es da draußen noch mehr. Hier: github.com/magnars/s.el#s-match-strings-all-regex-string Wie wäre es damit?
wvxvw

Antworten:

16

Hier ist, wie Sie es basierend auf Zeichenfolgen tun können, wie angefordert.

(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
quelle
Das sieht nicht ganz vollständig aus, könnten Sie dies zu einer voll funktionsfähigen Antwort erweitern?
Wasamasa
1
Der Code war vollständig, aber ich habe auch ein Anwendungsbeispiel hinzugefügt. Was möchten Sie noch sehen?
Alan Shutko
1
Diese Lösung ist leider zu einfach. Versuchen Sie es (re-seq "^.*$" ""). Gültiger regulärer Ausdruck, gültige Zeichenfolge, wird jedoch nie beendet.
Phil Lord
8

Es ist wahrscheinlich erwähnenswert, dass das Aufrufen occurmit dem universellen Argument dazu führt, dass der *Occur*Puffer nur mit Übereinstimmungen gefüllt wird - keine Dateinamen, Zeilennummern oder Header-Informationen. In Kombination mit einer Erfassungsgruppe kann so das gewünschte Muster extrahiert werden.

Wenn Sie z. B. C-u M-x occurgefolgt von \"\(.*\)\"eingeben, wird der Benutzer aufgefordert, welche Erfassungsgruppe erfasst werden soll (Standardeinstellung \1), und der Inhalt jeder in Anführungszeichen gesetzten Zeichenfolge wird in den *Occur*Puffer eingefügt.

Jack Rusher
quelle
5

Ich habe eine Emacs-Lisp-Antwort auf diese Frage: /codegolf//a/44319/18848

Mit derselben Struktur (while (search) (print)) können Sie sie in eine Funktion umwandeln, um Übereinstimmungen in einem Puffer in eine Liste zu verschieben und wie folgt zurückzugeben:

(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
quelle
Nizza Antwort, Anmerkung Sie können ersetzen möchten match-stringmit , match-string-no-propertiesso dass die Syntax Highlight nicht extrahiert wird. Möglicherweise möchten Sie ein übergeben regexp-group-index, damit Sie auswählen können, welcher Text gespeichert wird. Ebenso wie das Umkehren der Suchreihenfolge (die aktuelle Liste ist von Anfang bis Ende). Diese Antwort enthält eine geänderte Version. Emacs.stackexchange.com/a/38752/2418
ideasman42
3

Mit s.eldiesem habe schon kürzer, aber leider gibt es zu viele Übereinstimmungen:

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

Wenn dies in Ordnung ist (der reguläre Ausdruck für URLs ist sowieso nicht perfekt), könnte dies einfach kürzer sein, und wenn nicht, dann könnte ich es nicht kürzer machen als Alan Shutkos Antwort.

wvxvw
quelle
2

Lassen Sie mich nur erwähnen, warum ich denke, dass dies nicht im Kern implementiert ist. Einfach aus Effizienzgründen: Es ist nicht erforderlich, Listen zu kopieren, zu erstellen, weiterzugeben und durch Müll zu sammeln. Speichern Sie stattdessen die gesamte Zeichenfolge als Puffer und arbeiten Sie mit ganzzahligen Übereinstimmungsgrenzen. So occurfunktioniert es zum Beispiel: Es stimmt mit jeweils einer Zeichenfolge überein und fügt die Übereinstimmung ein *occur*. Es werden nicht alle Zeichenfolgen gleichzeitig abgeglichen, sie werden in die Liste aufgenommen, die einzufügende Liste wird durchlaufen, *occur*und die Liste und ihre Zeichenfolgen werden vom Müll gesammelt.

Genau wie Sie nicht (do (def x 1) (def x (+ 2 x)))in Clojure schreiben würden , sollten Sie standardmäßig nicht versuchen, Elisp wie eine funktionale Sprache zu verhalten. Ich würde es lieben, wenn es so wäre, aber wir müssen uns an dem ausrichten, was wir im Moment haben.

abo-abo
quelle
1

Wenn mir ein Plug erlaubt ist, schau in meine "m-buffer" Bibliothek.

(m-buffer-match buffer "foo")

Gibt eine Liste von Markern zurück, zu denen eine Übereinstimmung besteht foo.

Phil Lord
quelle