Esiste un modo idiomatico di leggere ciascuna riga in un buffer per elaborarla riga per riga?


11

In Python farei quanto segue per elaborare un file riga per riga:

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

Cercando di cercare come fare lo stesso in elisp (con buffer anziché file), non ho trovato un modo ovvio.

(Quello che voglio finire con sono due strutture di dati ordinate di linee, una con tutte le linee corrispondenti a una regex, l'altra contenente quelle che non corrispondevano.)

Risposte:


23

Esistono vari modi per farlo. Il modo di Kaushal può essere reso un po 'più efficiente, con:

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

Ma in Emacs è molto più consueto lavorare sul buffer piuttosto che sulle stringhe. Quindi, piuttosto che estrarre la stringa e poi lavorarci sopra, faresti semplicemente:

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

Inoltre, se vuoi operare su una regione piuttosto che sull'intero buffer, e se il tuo "operare" include la modifica del buffer, è frequente farlo all'indietro (in modo da non essere morso dal fatto che la "fine" "la posizione della tua regione si sposta ogni volta che modifichi il buffer):

(goto-char end)
(while (> (point) start)
  ...
  (forward-line -1))

Grazie per quei suggerimenti di ottimizzazione! Sempre buono da imparare da te.
Kaushal Modi,

A proposito dell'ultimo frammento, dovrebbe essere così (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1))):?
Kaushal Modi,

No, l'ultimo snippet lo presuppone starte endsono variabili esistenti che delimitano la regione su cui vogliamo operare.
Stefan

6

Non conosco alcun modo idiomatico ma mi è venuto in mente questo:

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

1

Penso che quanto segue sia il più idiomatico possibile:

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

EDIT: Ecco un'altra soluzione con loopal posto di dolist, e che classifica anche le linee in base al fatto che corrispondano o meno alla tua espressione regolare:

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

Se si imposta una variabile sull'output di questa funzione, ad esempio (setq x (loop ...)), verrà trovato l'elenco desiderato di righe corrispondenti (car x), con l'elenco delle righe non corrispondenti (cdr x).

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.