Emacs中文批处理文本的惯用方法?

11
在Python中,你可能会这样做
fout = open('out','w')
fin = open('in')
for line in fin:
    fout.write(process(line)+"\n")
fin.close()
fout.close()

(我认为其他许多编程语言也是类似的)。 在Emacs Lisp中,你会这样做

(find-file 'out')
(setq fout (current-buffer)
(find-file 'in')
(setq fin (current-buffer)
(while moreLines
 (setq begin (point))
 (move-end-of-line 1)
 (setq line (buffer-substring-no-properties begin (point))
 ;; maybe
 (print (process line) fout)
 ;; or
 (save-excursion 
  (set-buffer fout)
  (insert (process line)))
 (setq moreLines (= 0 (forward-line 1))))
(kill-buffer fin)
(kill-buffer fout)

以下代码灵感和实现来自于Emacs Lisp: 逐行处理文件。我是否应该尝试完全不同的方法?如何从打印语句中删除""

3个回答

38
如果您确实想要批处理 stdin 并将结果发送到 stdout,则可以使用 Emacs 的命令行选项--script,这将使您能够编写从stdin读取并将结果写入stdoutstderr的代码。
这是一个示例程序,类似于cat,但它会颠倒每一行的顺序:
#!/usr/local/bin/emacs --script
;;-*- mode: emacs-lisp;-*-

(defun process (string)
  "just reverse the string"
  (concat (nreverse (string-to-list string))))

(condition-case nil
    (let (line)
      ;; commented out b/c not relevant for `cat`, but potentially useful
      ;; (princ "argv is ")
      ;; (princ argv)
      ;; (princ "\n")
      ;; (princ "command-line-args is" )
      ;; (princ command-line-args)
      ;; (princ "\n")

      (while (setq line (read-from-minibuffer ""))
        (princ (process line))
        (princ "\n")))
  (error nil))

假设你有一个名为 stuff.txt 的文件,它包含了以下内容:

abcd
1234
xyz

假设你已经按照上面编写的shell脚本,命名为rcat,那么你可以通过以下方式调用它:

rcat < stuff.txt

你将看到以下内容被打印到标准输出:

dcba
4321
zyx

与普遍认为的相反,您实际上可以在stdin上进行批处理文件处理,而不必一次性读取整个文件。


4
请参考以下答案,了解如何将额外的命令行参数传递给 Emacs 脚本:https://dev59.com/K2025IYBdhLWcg3wEhhb - phils
1
你可以使用(ignore-errors ...)代替(condition-case ...),它们的作用相同。更好的做法是移除condition-case并将(read-from-minibuffer "")替换为(ignore-errors (read-from-minibuffer "")) - Flux

5

这是我想到的内容,对我来说更加符合习惯:

(with-temp-buffer
  (let ((dest-buffer (current-buffer)))
    (with-temp-buffer
      (insert-file-contents "/path/to/source/file")
      (while (search-forward-regexp ".*\n\\|.+" nil t)
        (let ((line (match-string 0)))
          (with-current-buffer dest-buffer
            (insert (process line)))))))
  (write-file "/path/to/dest/file" nil))

1

Emacs Lisp 不适合处理文件流。整个文件必须一次性读取:

(defun my-line-fun (line)
  (concat "prefix: " line))

(let* ((in-file "in")
       (out-file "out")
       (lines (with-temp-buffer 
        (insert-file-contents in-file)
        (split-string (buffer-string)  "\n\r?"))))
  (with-temp-file out-file
    (mapconcat 'my-line-fun lines "\n")))

1
split-string没有传入参数时,默认使用split-string-default-separators进行分割,该值默认为"[ \f\t\n\r\v]+"。你可能需要将"[\n\r]+"作为第二个参数显式地传入。 - haxney
1
从技术上讲,“Emacs Lisp不适合处理文件流”并不是真的;你可以使用进程过滤器,但这更加复杂,一次性读取整个文件可能是最简单的方法。如果确实需要读取流(例如网络套接字),则可能需要使用进程过滤器(请参阅Elisp手册)。 - haxney
感谢您修复了split-string的使用。 - Jürgen Hötzel
1
实际上,您可以处理stdin - Trey Jackson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接