¿Hay una forma idiomática de leer cada línea en un búfer para procesarla línea por línea?


11

En Python, haría lo siguiente para procesar un archivo línea por línea:

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

Intentando buscar cómo hacer lo mismo en elisp (con buffers en lugar de archivos), no encontré ninguna manera obvia.

(Con lo que quiero terminar es con dos estructuras de datos ordenadas de líneas, una con todas las líneas que coinciden con una expresión regular, y la otra con las que no coinciden).

Respuestas:


22

Hay varias formas de hacerlo. El camino de Kaushal puede hacerse un poco más eficiente, con:

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

Pero en Emacs es mucho más habitual trabajar en el búfer que en cadenas. Entonces, en lugar de extraer la cadena y luego trabajar en ella, simplemente haría:

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

Además, si desea operar en una región en lugar de en todo el búfer, y si su "operación" incluye la modificación del búfer, es frecuente hacerlo al revés (para que no le muerda el hecho de que el "fin" "la posición de su región se mueve cada vez que modifica el búfer):

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

¡Gracias por esos consejos de optimización! Siempre es bueno aprender de ti.
Kaushal Modi

Sobre el último fragmento, ¿debería ser de esta manera (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1))):?
Kaushal Modi

No, el último fragmento solo asume eso starty endson variables existentes que delimitan la región en la que queremos operar.
Stefan

6

No conozco ninguna forma idiomática, pero se me ocurrió esto:

(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

Creo que lo siguiente es tan idiomático como puede ser:

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

EDITAR: Aquí hay otra solución loopen lugar de dolist, y que también clasifica las líneas según si coinciden o no con su expresión regular:

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

Si establece una variable en la salida de esta función, por ejemplo (setq x (loop ...)), la lista deseada de líneas coincidentes se encontrará en (car x), con la lista de líneas no coincidentes (cdr x).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.