seby598
seby598

Reputation: 141

LISP: How to read content from a file and write it in another file?

I want to write a function that has as arguments the names of the two files and copies the content from the first file to the second one.

So far I wrote a function that reads from a file:

(defun readFile (name)
 (let ((in (open name)))
  (format t "~a~%" (read-line in))
   (close in)))

And a function that writes a string to a file:

(defun writeFile (name content)
(with-open-file (stream name
    :direction :output
    :if-exists :overwrite
    :if-does-not-exist :create)
(format stream content)))

Following Savantes instructions I wrote the function again and this is how it looks:

(defun read-write-to-file (input-file output-file)
(WITH-OPEN-FILE (output-stream output-file
         :direction :output
         :if-exists :new-version
         :if-does-not-exist :create)
  (WITH-OPEN-FILE (input-stream input-file
                 :direction :input)
        (FORMAT output-stream "~a" (READ input-stream nil 'eof))
)))

Now the only problem is that it doesn't read the entire file.

Upvotes: 3

Views: 5998

Answers (5)

Peter
Peter

Reputation: 140

This function copies the content from the first file to the second one:

(defun my-function (first second)
  (cl-fad:copy-file first second))

(of course, the cl-fad package must be loaded)

Peter

Upvotes: 2

gsl
gsl

Reputation: 1143

Possibly a more concise solution, at least as fast as the above, use copy-stream-to-stream from the excellent UIOP package, included with ASDF (and therefore with Quicklisp):

(require 'uiop)
(defun file-copy (source destination)
  (with-open-file (in source :direction :input)
    (with-open-file (out destination :direction :output)
      (uiop:copy-stream-to-stream in out))))

It should be as fast (if not more), as it uses WRITE-SEQUENCE as default. See the first link for additional options to copy-stream-to-stream.

Upvotes: 4

Svante
Svante

Reputation: 51501

You found with-open-file. Use it for input and output. Don't use open and close instead.

Open both files, then write to the output stream what you read-line from the input stream.

Your WRITEFILE obviously does not compile, by the way.

Also, please use proper names and indentation.

EDIT after question update:

Read-line reads one line. You can write it with write-line, so that your line endings are kept. You need to do that in a loop in order to copy more lines.

Hint: You should write your names in lower case in the code. The reader automatically upcases them. I chose to write WRITEFILE above to show how your chosen name is treated internally. Capitalization is not relevant. L and l are the same. Parts of names are conventionally separated by hyphens in Lisp code.

Upvotes: 4

user2240219
user2240219

Reputation: 166

The Common Lisp cookbook actually contains an answer to your question:

http://cl-cookbook.sourceforge.net/io.html

See the "Bulk I/O" section in the bottom of that page.

With minor corrections and modifications, the code will look like:

(defun my-copy-file (from-file to-file)
  (with-open-file (input-stream from-file
                :direction :input
                :element-type '(unsigned-byte 8))
    (with-open-file (output-stream to-file
                   :direction :output
                   :if-exists :supersede
                   :if-does-not-exist :create
                   :element-type '(unsigned-byte 8))
      (let ((buf (make-array 4096 :element-type (stream-element-type input-stream))))
    (loop for pos = (read-sequence buf input-stream)
       while (plusp pos)
       do (write-sequence buf output-stream :end pos))))))

This should be able to handle text files as well as binary files.

Upvotes: 5

Thomas Bartscher
Thomas Bartscher

Reputation: 188

First: Don't use 'READ' for that purpose, use 'READ-LINE'. 'READ' executes read-macros, which opens a security hole. Conversely you need to use 'WRITE-LINE'.

When you use 'READ-LINE' you should see that it does exactly that: it reads exactly one line and returns that or (in your case) the symbol 'EOF' when there is no line to read left. It also advances your read pointer after the next 'EOL' (or something similar, I am not well versed with the implementation specifics of streams), so when you do your next 'READ-LINE' on that stream the next line is read. You now just need to repeatedly read and write until you encounter the end of the file.

Upvotes: 1

Related Questions