Existe-t-il une manière idiomatique de lire chaque ligne dans un tampon pour la traiter ligne par ligne?

11

En Python, je ferais ce qui suit pour traiter un fichier ligne par ligne:

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

En essayant de chercher comment faire la même chose dans elisp (avec des tampons au lieu de fichiers), je n'ai trouvé aucun moyen évident.

(Ce que je veux finir, c'est deux infrastructures de données ordonnées de lignes, l'une avec toutes les lignes correspondant à une expression régulière, l'autre contenant celles qui ne correspondent pas.)

Le chat unfun
la source

Réponses:

23

Il existe différentes façons de procéder. La voie de Kaushal peut être rendue un peu plus efficace, avec:

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

Mais dans Emacs, il est beaucoup plus courant de travailler sur le tampon plutôt que sur les chaînes. Donc, plutôt que d'extraire la chaîne et de travailler dessus, vous devriez simplement faire:

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

De plus, si vous voulez opérer sur une région plutôt que sur tout le tampon, et si votre "opération" inclut la modification du tampon, il est fréquent de le faire à l'envers (pour ne pas vous faire mordre par le fait que la "fin "la position de votre région se déplace à chaque fois que vous modifiez le tampon):

(goto-char end)
(while (> (point) start)
  ...
  (forward-line -1))
Stefan
la source
Merci pour ces conseils d'optimisation! Toujours bon d'apprendre de vous.
Kaushal Modi
A propos du dernier extrait, devrait-il être de cette façon (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1))):?
Kaushal Modi
Non, le dernier extrait suppose simplement que startet endsont des variables existantes qui délimitent la région sur laquelle nous voulons opérer.
Stefan
6

Je ne connais aucun moyen idiomatique mais je suis venu avec ceci:

(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
la source
1

Je pense que ce qui suit est aussi idiomatique que possible:

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

EDIT: Voici une autre solution avec loopà la place de dolist, et qui classe également les lignes selon qu'elles correspondent ou non à votre expression régulière:

(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 vous définissez une variable sur la sortie de cette fonction, par exemple (setq x (loop ...)), la liste souhaitée des lignes correspondantes se trouvera dans (car x), la liste des lignes non correspondantes étant (cdr x).

Ruy
la source