Gibt es eine idiomatische Möglichkeit, jede Zeile in einem Puffer zu lesen, um sie zeilenweise zu verarbeiten?

11

In Python würde ich Folgendes tun, um eine Datei Zeile für Zeile zu verarbeiten:

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

Beim Versuch, nachzuschlagen, wie man dasselbe in elisp macht (mit Puffern anstelle von Dateien), fand ich keinen offensichtlichen Weg.

(Am Ende möchte ich zwei geordnete Datenstrukturen von Zeilen haben, von denen eine mit allen Zeilen übereinstimmt, die mit einem regulären Ausdruck übereinstimmen, und die andere diejenigen enthält, die nicht übereinstimmen.)

Die Unfun Cat
quelle

Antworten:

22

Es gibt verschiedene Möglichkeiten, dies zu tun. Kaushals Weg kann ein gutes Stück effizienter gestaltet werden, mit:

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

In Emacs ist es jedoch viel üblicher, am Puffer zu arbeiten, als an Zeichenfolgen. Anstatt den String zu extrahieren und dann daran zu arbeiten, tun Sie einfach Folgendes:

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

Wenn Sie nicht in der gesamten Puffer, sondern in einer Region arbeiten möchten und Ihre "Operation" das Ändern des Puffers umfasst, geschieht dies häufig rückwärts (damit Sie nicht von der Tatsache gebissen werden, dass das "Ende" "Die Position Ihrer Region verschiebt sich jedes Mal, wenn Sie den Puffer ändern.):

(goto-char end)
(while (> (point) start)
  ...
  (forward-line -1))
Stefan
quelle
Vielen Dank für diese Optimierungstipps! Immer gut von dir zu lernen.
Kaushal Modi
Sollte es beim letzten Ausschnitt so sein : (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1)))?
Kaushal Modi
Nein, der letzte Schnipsel davon ausgegangen , dass nur startund endvorhandene Variablen , die die Region begrenzen , auf die wir arbeiten wollen.
Stefan
6

Ich kenne keinen idiomatischen Weg, aber ich habe mir Folgendes ausgedacht:

(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
quelle
1

Ich denke, das Folgende ist so idiomatisch wie es nur geht:

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

BEARBEITEN: Hier ist eine andere Lösung mit loopanstelle von dolistund klassifiziert die Zeilen auch danach, ob sie mit Ihrem regulären Ausdruck übereinstimmen oder nicht:

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

Wenn Sie beispielsweise eine Variable für die Ausgabe dieser Funktion festlegen (setq x (loop ...)), wird die gewünschte Liste der übereinstimmenden Zeilen in gefunden (car x), wobei die Liste der nicht übereinstimmenden Zeilen angezeigt wird (cdr x).

Ruy
quelle