Czy istnieje idiomatyczny sposób odczytu każdej linii w buforze w celu przetworzenia jej linia po linii?

11

W Pythonie wykonaj następujące czynności, aby przetworzyć plik linia po linii:

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

Próbując sprawdzić, jak zrobić to samo w elisp (z buforami zamiast plików), nie znalazłem oczywistego sposobu.

(Chciałbym skończyć z dwiema uporządkowanymi strukturami danych linii, jedną ze wszystkimi liniami pasującymi do wyrażenia regularnego, drugą zawierającą te, które nie pasują.)

The Unfun Cat
źródło

Odpowiedzi:

23

Można to zrobić na różne sposoby. Sposób Kaushala może być nieco bardziej wydajny dzięki:

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

Ale w Emacsie znacznie bardziej zwyczajowo pracuje się na buforze niż na łańcuchach. Zamiast wyodrębniać ciąg, a następnie pracować nad nim, wystarczy wykonać:

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

Ponadto, jeśli chcesz operować na regionie, a nie na całym buforze, a jeśli twoje „operowanie” obejmuje modyfikowanie bufora, często robi się to wstecz (aby nie dać się pogryźć faktem, że „koniec” „pozycja regionu zmienia się za każdym razem, gdy modyfikujesz bufor):

(goto-char end)
(while (> (point) start)
  ...
  (forward-line -1))
Stefan
źródło
Dziękujemy za porady dotyczące optymalizacji! Zawsze dobrze się od ciebie uczyć.
Kaushal Modi
O ostatnim fragmencie, powinien on być w ten sposób: (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1)))?
Kaushal Modi
No, ostatni fragment po prostu zakłada, że starti endsą istniejące zmienne, które ograniczają obszar, na którym chcemy pracować.
Stefan
6

Nie znam żadnego idiomatycznego sposobu, ale wpadłem na to:

(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
źródło
1

Myślę, że następujące jest tak idiomatyczne, jak to tylko możliwe:

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

EDYCJA: Oto inne rozwiązanie loopzamiast dolist, i które również klasyfikuje linie według tego, czy pasują one do wyrażenia regularnego:

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

Jeśli ustawisz zmienną na wyjściu tej funkcji, powiedzmy (setq x (loop ...)), wtedy żądana lista pasujących linii zostanie znaleziona w (car x), z listą niepasujących linii (cdr x).

Ruy
źródło