Чи існує ідіоматичний спосіб читання кожного рядка в буфері, щоб обробити його рядок за рядком?


11

У Python я б зробив наступне, щоб обробити файл за рядком:

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

Намагаючись знайти, як це зробити в elisp (з буферами замість файлів), я не знайшов очевидного способу.

(Я хочу закінчити - це дві впорядковані структури даних рядків, одна з яких всі рядки відповідають регулярному вираженню, а друга містить ті, що не збігаються.)

Відповіді:


23

Існують різні способи зробити це. Шлях Каушала можна зробити трохи ефективнішим:

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

Але в Emacs набагато звичніше працювати над буфером, ніж над рядками. Тому замість того, щоб витягувати рядок і потім працювати над нею, ви просто зробите:

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

Крім того, якщо ви хочете оперувати регіоном, а не цілим буфером, і якщо ваше "оперування" включає в себе зміну буфера, це часто робиться назад (щоб вас не покусав той факт, що "кінець" "положення вашого регіону переміщується щоразу, коли ви змінюєте буфер):

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

Дякуємо за поради щодо оптимізації! Завжди добре вчитися у вас.
Каушал Моді

Щодо останнього фрагмента, чи має бути так (let ((start (point))) (goto-char (point-max)) (while (> (point) start) ... (forward-line -1))):?
Каушал Моді

Ні, останній фрагмент просто передбачає, що це startі endє існуючі змінні, які розмежовують область, в якій ми хочемо оперувати.
Стефан

6

Я не знаю жодного ідіоматичного способу, але я придумав це:

(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

Я думаю, що настільки ж ідіоматичне, як це може бути:

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

EDIT: Ось ще одне рішення із loopзамість dolist, і яке також класифікує рядки відповідно до того, відповідають вони чи вашому регулярному виразу:

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

Якщо встановити змінну для виводу цієї функції, скажімо (setq x (loop ...)), тоді потрібний список відповідних рядків буде знайдений у (car x)списку, що буде невідповідним рядком (cdr x).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.