Есть ли идиоматический способ чтения каждой строки в буфере, чтобы обрабатывать ее построчно?

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

РЕДАКТИРОВАТЬ: Вот другое решение с 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).

Рюи
источник