Dapatkan semua kecocokan regexp dalam buffer sebagai daftar

18

Di situs Code Golf Stack Exchange hari ini, saya menemukan jawaban ini di Clojure untuk pertanyaan "Dapatkan semua tautan di laman web".

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

Tanpa makro mewah, hanya ini:

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

Ini mengembalikan daftar:

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

Bisakah saya melakukan hal serupa di Emacs Lisp?

Mungkin fungsi seperti (re-seq regexp (buffer-string))itu kembali '(firstmatch secondmatch thirdmatch ...)?

pengasuh
sumber
Inilah yang M-x occurdilakukan, tetapi saya akan mencari ke dalam untuk fungsi yang lebih rendah untuk melakukannya.
wvxvw
@ wvxvw Poin yang bagus, saya bahkan tidak memikirkannya occur. Saya harus memeriksa sumbernya.
pengasuh
Saya melihat ke dalam, dan oh celakalah, kode itu melakukan terlalu banyak dan itu tidak mudah untuk digunakan kembali, tidak sama sekali. Kandidat saya berikutnya adalah s.el, tetapi mungkin ada lebih banyak di luar sana. Di sini: github.com/magnars/s.el#s-match-strings-all-regex-string bagaimana dengan ini?
wvxvw

Jawaban:

16

Inilah cara Anda dapat melakukannya berdasarkan string, seperti yang diminta.

(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
sumber
Itu terlihat tidak cukup lengkap, dapatkah Anda memperluas ini ke jawaban yang berfungsi penuh?
wasamasa
1
Kode lengkap, tetapi saya juga menambahkan contoh penggunaan. Apa lagi yang ingin Anda lihat?
Alan Shutko
1
Solusi ini terlalu sederhana, sayangnya. Coba (re-seq "^.*$" ""). Regexp valid, string yang valid, tetapi tidak pernah berakhir.
Phil Lord
8

Mungkin perlu dicatat bahwa menggunakan occurargumen universal menyebabkannya mengisi *Occur*buffer hanya dengan kecocokan - tanpa nama file, nomor baris, atau informasi header. Ketika dikombinasikan dengan kelompok tangkap, ini memungkinkan seseorang untuk mengekstrak pola apa pun yang diinginkan.

Misalnya, C-u M-x occurdiikuti oleh \"\(.*\)\"akan meminta pengguna untuk mengumpulkan kelompok yang mana (default \1), dan kemudian menempatkan konten setiap string yang dikutip ke dalam *Occur*buffer.

Jack Rusher
sumber
5

Saya punya jawaban emacs lisp untuk pertanyaan yang diposting: /codegolf//a/44319/18848

Dengan menggunakan struktur (sementara (pencarian) (cetak)) yang sama, Anda dapat memodifikasinya menjadi fungsi untuk mendorong kecocokan dalam buffer ke daftar dan mengembalikannya seperti ini:

(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
sumber
Jawaban yang bagus, perhatikan Anda mungkin ingin mengganti match-stringdengan itu match-string-no-propertiessehingga sintaks highlight tidak diekstraksi. Anda mungkin ingin meneruskan regexp-group-indexpenggunaan agar Anda dapat memilih teks mana yang disimpan. Serta membalikkan urutan pencarian (daftar saat ini adalah yang terakhir ke yang pertama). Lihat jawaban ini yang mencakup versi modifikasi emacs.stackexchange.com/a/38752/2418
ideasman42
3

Menggunakan s.elini akan lebih pendek, tapi, sayangnya, ini memberikan terlalu banyak kecocokan:

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

Jika ini ok (regex untuk URL tidak sempurna), ini mungkin lebih pendek, dan jika tidak, maka saya tidak berpikir saya bisa membuatnya lebih pendek dari jawaban Alan Shutko.

wvxvw
sumber
2

Biarkan saya sebutkan mengapa saya pikir ini tidak diterapkan dalam inti. Hanya untuk alasan efisiensi: tidak perlu menyalin, membuat daftar, menyebarkannya dan mengumpulkannya. Alih-alih, simpan seluruh string sebagai buffer, dan beroperasi dengan batas pencocokan integer. Begitulah cara occurkerjanya, misalnya: itu cocok satu string pada satu waktu dan memasukkan kecocokan ke dalam *occur*. Itu tidak cocok dengan semua string sekaligus, membuatnya menjadi daftar, loop pada daftar untuk dimasukkan ke dalam *occur*dan sampah mengumpulkan daftar dan string-nya.

Sama seperti Anda tidak akan menulis (do (def x 1) (def x (+ 2 x)))di Clojure, Anda seharusnya tidak mencoba mencoba agar Elisp berperilaku seperti bahasa fungsional. Saya akan senang jika itu, tetapi kita harus membuat sesuai dengan apa yang kita miliki saat ini.

abo-abo
sumber
1

Jika saya mungkin diizinkan plug, lihat perpustakaan "m-buffer" saya.

(m-buffer-match buffer "foo")

Mengembalikan daftar penanda yang cocok ke foo.

Phil Lord
sumber