3

替换文件中字符串的lisp方式是什么。

有一个由 标识的文件*file-path*,一个搜索字符串*search-term*和一个替换字符串*replace-term*

如何使用*search-term*s 替换所有 s 实例的文件*replace-term*,最好代替旧文件?

4

3 回答 3

2

再来解决这个问题,但首先很少有警告:

  1. 为了使它在现实生活中真正健壮和可用,您需要将其包装handler-case并处理各种错误,例如磁盘空间不足、设备未准备好、读/写权限不足、为缓冲区分配的内存不足等在。

  2. 这不做类似正则表达式的替换,它是简单的字符串替换。对大文件进行基于正则表达式的替换可能看起来远没有从一开始看起来那么简单,值得编写一个单独的程序,比如 sed 或 awk 或整个语言,如 Perl 或 awk ;)

  3. 与其他解决方案不同,它将在要替换的文件附近创建一个临时文件,并将到目前为止处理的数据保存到该文件中。从某种意义上说,这可能会更糟,因为它将使用更多的磁盘空间,但这更安全,因为如果程序在中间失败,原始文件将保持不变,不仅如此,您可以稍后再恢复替换临时文件,例如,如果您也将偏移量保存到临时文件中的原始文件中。


(defun file-replace-string (search-for replace-with file
                            &key (element-type 'base-char)
                              (temp-suffix ".tmp"))
  (with-open-file (open-stream
                   file
                   :direction :input
                   :if-exists :supersede
                   :element-type element-type)
    (with-open-file (temp-stream
                     (concatenate 'string file temp-suffix)
                     :direction :output
                     :element-type element-type)
      (do ((buffer (make-string (length search-for)))
           (buffer-fill-pointer 0)
           (next-matching-char (aref search-for 0))
           (in-char (read-char open-stream nil :eof)
                    (read-char open-stream nil :eof)))
          ((eql in-char :eof)
           (when (/= 0 buffer-fill-pointer)
             (dotimes (i buffer-fill-pointer)
               (write-char (aref buffer i) temp-stream))))
        (if (char= in-char next-matching-char)
            (progn
              (setf (aref buffer buffer-fill-pointer) in-char
                    buffer-fill-pointer (1+ buffer-fill-pointer))
              (when (= buffer-fill-pointer (length search-for))
                (dotimes (i (length replace-with))
                  (write-char (aref replace-with i) temp-stream))
                (setf buffer-fill-pointer 0)))
            (progn
              (dotimes (i buffer-fill-pointer)
                (write-char (aref buffer i) temp-stream))
              (write-char in-char temp-stream)
              (setf buffer-fill-pointer 0)))
        (setf next-matching-char (aref search-for buffer-fill-pointer)))))
  (delete-file file)
  (rename-file (concatenate 'string file temp-suffix) file))
于 2012-09-01T12:55:51.233 回答
1

它可以通过多种方式完成,例如使用正则表达式。我看到的最独立的方式如下所示:

(defun replace-in-file (search-term file-path replace-term)
  (let ((contents (rutil:read-file file-path)))
    (with-open-file (out file-path :direction :output :if-exists :supersede)
      (do* ((start 0 (+ pos (length search-term)))
            (pos (search search-term contents)
                 (search search-term contents :start2 start)))
           ((null pos) (write-string (subseq contents start) out))
        (format out "~A~A" (subseq contents start pos) replace-term))))
  (values))

请参阅rutil:read-file此处的实现:https ://github.com/vseloved/rutils/blob/master/core/string.lisp#L33

另请注意,此功能将用任何字符(包括换行符)替换搜索词。

于 2012-08-31T07:51:17.140 回答
0

在带有 ireggex 鸡蛋的鸡肉计划中:

(use irregex) ; irregex, the regular expression library, is one of the
              ; libraries included with CHICKEN.

(define (process-line line re rplc) 
  (irregex-replace/all re line rplc))

(define (quickrep re rplc) 
  (let ((line (read-line)))
    (if (not (eof-object? line))
        (begin 
          (display (process-line line re rplc))
          (newline)
          (quickrep re rplc)))))

(define (main args)
  (quickrep (irregex (car args)) (cadr args)))

编辑:在上面的示例中缓冲输入不允许正则表达式跨越多行。
为了解决这个问题,这里有一个更简单的实现,它将整个文件扫描为一个字符串:

(use ireggex)
(use utils)

(define (process-line line re rplc) 
  (irregex-replace/all re line rplc))

(define (quickrep re rplc file) 
  (let ((line (read-all file)))
      (display (process-line line re rplc))))

(define (main args)
  (quickrep (irregex (car args)) (cadr args) (caddr args)))
于 2012-08-31T23:03:58.857 回答