Replace regex in file, in-place, with Common Lisp

I'm trying to write a Common Lisp version of Python regex search and replace using in-place file modification:

import fileinput, re

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

      

This is the generic Lisp code I was able to come up with:

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

      

It works like. Currently, the replaced text will be appended to the end of the file. My immediate problem is that I cannot figure out how to replace the content.

To better explain if it file.txt

contains:

1 foo
2 bar
3 foobar

      

after calling

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

      

I get:

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

      

Instead of correct replacement:

1 bar
2 bar
3 barbar

      

I tried with all possible options with-open-file

(from Successful Lisp ), with no success:

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)

      

Can anyone please send me in the right direction for the function to render in the file.txt

correct way?

Also, what would a normal Lisp idiomatic way of doing this be, assuming of course cl-ppcre

?

Is there a more concise way to do in-place regex replacement using Common Lisp?

+3


source to share


1 answer


There is no primitive operation in Python that modifies a file in place; instead, there is a helper function fileinput

that gives the illusion of modifying the file in place by first copying the file to the backup file and then reading the backup file and writing the result back to the original. From the manual :

Additional in-place filtering: if a keyword argument is passed inplace=1

before fileinput.input()

or to a constructor fileinput

, the file is moved to the backup file and standard output to the input file (if a file with the same name as the backup file already exists, it will be silently replaced) ... This allows you to write a filter that overwrites its input file in place. If the backup option is given (usually as backup = '.'), It specifies the extension for the backup file, and the backup file stays around; the default extension is ".bak", and it is removed when the output file is closed. In-place filtering is disabled when reading stdin.



So, the way to do this operation in Common Lisp is to mimic Python code by first copying the file into a backup file, for example using this function my-copy-file

, and then write the following code:

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

      

+8


source







All Articles