2015-07-19 47 views
3

我想寫Python的正則表達式搜索的Common Lisp的版本和替換,以文件的就地修改:替換正則表達式,就地,與Common Lisp的

import fileinput, re 

for line in fileinput.input(inplace=1, backup='.bak'): 
    line = re.sub(r"foo", "bar", line, re.M) 
print (line) 

這是常見的Lisp代碼我能想出來:

(require :cl-ppcre) 

(defun in-place-subst (file) 
    (with-open-file (stream file :direction :io :if-exists :overwrite) 
    (loop for line = (read-line stream nil) 
     while line do 
     (write-line (cl-ppcre:regex-replace-all "foo" line "bar") stream)))) 

它的工作,有點。現在替換文本將被添加到文件末尾。我眼前的問題是我無法弄清楚如何將替換成的內容。

爲了更好的解釋,如果file.txt包含:

1 foo 
2 bar 
3 foobar 

調用

(in-place-subst "file.txt") 

後,我得到:

1 foo 
2 bar 
3 foobar 
1 bar 
2 bar 
3 barbar 

取而代之的是正確的更換:

1 bar 
2 bar 
3 barbar 

我試圖用一切可能的with-open-file選項(從Successful Lisp),沒有成功:

Keyword  Value    Action if File Exists 
---------- ------------------ --------------------------------------- 
:IF-EXISTS NIL     return NIL 
:IF-EXISTS :ERROR    signal an error 
:IF-EXISTS :NEW-VERSION   next version (or error) 
:IF-EXISTS :RENAME    rename existing, create new 
:IF-EXISTS :SUPERSEDE   replace file upon CLOSE 
:IF-EXISTS :RENAME-AND-DELETE rename and delete existing, create new 
:IF-EXISTS :OVERWRITE   reuse existing file (position at start) 
:IF-EXISTS :APPEND    reuse existing file (position at end) 

可能有人請給我在正確的方向,使該功能將在正確的方式呈現file.txt

另外,什麼是常見Lisp 慣用這樣做的方式,當然假設cl-ppcre可用?

是否有更簡潔的方式使用Common Lisp進行就地正則表達式替換?

+1

'FILE-POSITION'讀取並設置文件位置。你可能想試驗一下。 –

+0

謝謝您的建議。我嘗試過使用'(文件位置流:啓動)',嘗試重置流的文件位置,沒有太多進展。我相信可以用更多的代碼來做到這一點(也許在內存中做一個副本,做替換和寫回),但是我希望找到一個更簡潔,也許更習慣的方式。 – gsl

+0

保存文件位置。閱讀該行。將文件位置重置爲開頭。寫新的一行。請注意,它不會更改行的大小或擴展文件。如果你的更換時間更短或更長,那麼你需要處理... –

回答

8

在Python中,沒有原始操作可以修改文件「in-place」; 有幫助程序類fileinput的功能,它給出了 通過首先將文件複製到備份文件來修改文件的錯覺,然後 讀取備份文件並將處理結果寫入原來的一個。從manual

可選就地過濾:如果inplace=1傳遞 到fileinput.input()或到FileInput構造函數,則文件被移動 到備份文件和標準輸出關鍵字參數被引導到輸入文件 (如果一個與備份文件同名的文件已經存在,則該文件將被無提示地替換) 。 這使得可以編寫一個過濾器來重寫其輸入文件。 如果給出備份參數(通常爲backup ='。'),則它指定備份文件的擴展名,備份文件保留爲 ;默認情況下,擴展名是'.bak',並且在輸出 文件關閉時刪除。當讀取標準輸入時,就地過濾被禁用。

所以,做Common Lisp中這種操作的方式是模仿 Python code,先用這個functionmy-copy-file將文件複製到備份文件,例如,然後寫了下面的代碼:

(defun in-place-subst (file) 
    (let ((backup-file (concatenate 'string file ".bak"))) 
    (my-copy-file file backup-file) 
    (with-open-file (in-stream backup-file) 
     (with-open-file (out-stream file :direction :output :if-exists :supersede) 
     (loop for line = (read-line in-stream nil) 
      while line do 
      (write-line (cl-ppcre:regex-replace-all "foo" line "bar") out-stream)))))) 
+0

謝謝你的信息和漂亮的代碼! – gsl