ファイル内の文字列を置き換える Lisp の方法は何ですか。
*file-path*
、検索文字列*search-term*
、および置換文字列 で識別されるファイルがあります*replace-term*
。
できれば古いファイルの代わりに、 s のすべてのインスタンスを*search-term*
s に置き換えたファイルを作成する方法は?*replace-term*
問題をもう 1 つ取り上げますが、最初にいくつかの警告があります。
これを実際の状況で本当に堅牢で使用できるようにするには、これをラップしてhandler-case
、ディスク容量不足、デバイスの準備ができていない、読み取り/書き込みの権限が不十分、バッファに割り当てるメモリが不足などのさまざまなエラーを処理する必要があります。の上。
これは正規表現のような置換ではなく、単純な文字列置換です。大きなファイルで正規表現ベースの置換を行うことは、最初から思っているよりもはるかに簡単に見えるかもしれません.sedやawkのような別のプログラム、またはPerlやawkのような言語全体を書く価値があります;)
他のソリューションとは異なり、置き換えられるファイルの近くに一時ファイルが作成され、これまでに処理されたデータがこのファイルに保存されます。これは、より多くのディスク容量を使用するという意味でより悪いかもしれませんが、プログラムが途中で失敗した場合、元のファイルがそのまま残るため、より安全です。たとえば、オフセットを一時ファイルの元のファイルにも保存していた場合は、一時ファイル。
(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))
これは、正規表現など、さまざまな方法で実現できます。私が見る最も自己完結型の方法は、次のようなものです。
(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
また、この関数は検索語を改行を含む任意の文字に置き換えることに注意してください。
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)))
編集:上記の例では、入力をバッファリングすると、正規表現が多くの行にまたがることができなくなります。
これに対抗するために、ファイル全体を 1 つの文字列としてスキャンするさらに単純な実装を次に示します。
(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)))