Subject: Re: reading a file into a string
From: Erik Naggum <erik@naggum.net>
Date: Sat, 02 Feb 2002 03:52:26 GMT
Newsgroups: comp.lang.lisp
Message-ID: <3221610744163603@naggum.net>

* Dr. Edmund Weitz
| I want to read a whole (text) file into a string.  And I wonder what
| would be the best way to do it.

  The function is named file-contents in Allegro CL and is based on an
  earlier version of mine, which goes like this:

(defun file-contents (pathname &rest open-arguments)
  "Map the current contents of the file named by PATHNAME into a vector,
which is returned.  OPEN-ARGUMENTS is a list of arguments passed to OPEN;
it should not include a :DIRECTION argument.  An :ELEMENT-TYPE argument
also specifies the type of the returned vector.  Returns NIL if the file
does not exist and :IF-DOES-NOT-EXIST is NIL."
  (with-open-stream (stream (apply #'open pathname :direction :input open-arguments))
    (when stream
      (let ((buffer (make-array (file-length stream) :element-type (stream-element-type stream))))
	(if (= (read-sequence buffer stream) (length buffer))
	  buffer
	  (error 'file-error
                 :format-control "Incomplete READ-SEQUENCE from ~S."
                 :format-arguments (list (pathname stream))
                 :pathname (pathname stream)))))))

(defun (setf file-contents) (vector pathname &rest open-arguments)
  "Make the contents of the file named by PATHNAME become that of VECTOR,
which is returned.  OPEN-ARGUMENTS is a list of arguments passed to OPEN;
it should not include either a :DIRECTION or an :ELEMENT-TYPE argument.
Does nothing and returns NIL if the file exists and :IF-EXISTS is NIL.
:IF-EXISTS defaults to :SUPERSEDE."
  (with-open-stream (stream (apply #'open pathname
				   :direction :output
				   :element-type (array-element-type vector)
				   (append open-arguments '(:if-exists :supersede))))
    (when stream
      (write-sequence vector stream))))

  Allegro CL has very strong support for various external-formats, so if
  you specify (or default to) characater as the element type, this may do
  some work between the read system call and read-sequence, so although it
  is very fast and efficient, it is hard to beat a byte-for-byte copy --
  check which external-formats are available and apply  If you want
  byte-for-byte copies, use (unsigned-byte 8) as the element-type.  The
  portable way to convert between (unsigned-byte 8) and character is to use
  (map #'string #'code-char <byte-vector>), but you can usually find a way
  to hack the type system so such a byte vector turns into a string, but
  this is not portable, of course.  Please remember that element-type and
  external-format are available keyword arguments to open.

| 2. Is there an ANSI-compliant solution that is comparable in
|    performance?

  The above should be.  Notice that file-length is a standard function, but
  it applies to a stream, not to pathnames.

///                                                             2002-02-02
-- 
  In a fight against something, the fight has value, victory has none.
  In a fight for something, the fight is a loss, victory merely relief.