Apakah ada cara idiomatis membaca setiap baris dalam buffer untuk memprosesnya baris demi baris?

11

Dengan Python saya akan melakukan hal berikut untuk memproses file baris demi baris:

with open(infile) as f:
    for line in f:
        process(line)

Mencoba mencari cara melakukan hal yang sama di elisp (dengan buffer bukan file), saya tidak menemukan cara yang jelas.

(Yang ingin saya selesaikan adalah dua struktur data garis yang dipesan, satu dengan semua garis yang cocok dengan regex, yang lain berisi yang tidak cocok.)

Kucing Unfun
sumber

Jawaban:

23

Ada berbagai cara untuk melakukannya. Cara Kaushal dapat dibuat sedikit lebih efisien, dengan:

(goto-char (point-min))
(while (not (eobp))
  (let ((line (buffer-substring (point)
                                (progn (forward-line 1) (point)))))
    ...))

Tetapi dalam Emacs, lebih umum untuk bekerja pada buffer daripada pada string. Jadi daripada mengekstrak string dan kemudian bekerja di atasnya, Anda hanya perlu:

(goto-char (point-min))
(while (not (eobp))
  ...
  (forward-line 1))

Juga, jika Anda ingin beroperasi di suatu wilayah daripada di seluruh buffer, dan jika "operasi" Anda termasuk memodifikasi buffer, itu sering dilakukan mundur (sehingga Anda tidak digigit oleh fakta bahwa "akhir" "posisi wilayah Anda bergerak setiap kali Anda memodifikasi buffer):

(goto-char end)
(while (> (point) start)
  ...
  (forward-line -1))
Stefan
sumber
Terima kasih atas kiat pengoptimalan itu! Selalu baik untuk belajar dari Anda.
Kaushal Modi
Tentang potongan terakhir, harus itu seperti ini: (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1)))?
Kaushal Modi
Tidak, cuplikan terakhir hanya mengasumsikan itu startdan endmerupakan variabel yang ada yang membatasi wilayah tempat kami ingin beroperasi.
Stefan
6

Saya tidak tahu ada cara idiomatik tapi saya datang dengan ini:

(defun my/walk-line-by-line ()
  "Process each line in the buffer one by one."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (not (eobp))
      (let* ((lb (line-beginning-position))
             (le (line-end-position))
             (ln (buffer-substring-no-properties lb le)))
        (message ">> %s" ln) ; Replace this with any processing function you like
        (forward-line 1)))))
Kaushal Modi
sumber
1

Saya pikir yang berikut ini sebodoh yang bisa didapat:

(dolist (line (split-string (buffer-string) "\n")) 
  ... process line here ...
  )

EDIT: Ini adalah solusi lain loopuntuk menggantikannya dolist, dan yang juga mengklasifikasikan garis berdasarkan apakah mereka cocok dengan ekspresi reguler Anda atau tidak:

(loop for line in (split-string (buffer-string) "\n")
  if (string-match "your-regexp" line)
    collect line into matching
  else
    collect line into nonmatching
  finally return (cons matching nonmatching)
  )

Jika Anda menetapkan variabel ke output fungsi ini, katakanlah (setq x (loop ...)), maka daftar baris yang cocok akan ditemukan di (car x), dengan daftar baris yang tidak cocok (cdr x).

Ruy
sumber