This thread
http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/2bde165d6e368cae/5e9cc62893c3c3e2?lnk=gst&q=read+columns#
discussed reading columns from a file (among other things, ruby
included)
The lisp ideas used `read' to advance from field to field in a file.
I have a slightly different file structure (in tecplot format), where
the file has two headers that I first have to parse in order to parse
the columns. So, I have to read lines, and then decide what to do.
(There is another solution that I discuss later on).
Here is my code so far:
(with-open-file (stream file :direction :input
:if-does-not-exist :error)
(let (header vars c-vars data)
(loop for line = (read-line stream nil 'eof)
until (eq line 'eof)
do (cond
((string= "#" (subseq line 0 1)) (push line header))
((string= "VARIABLES" (subseq line 0 9))
(setf vars (split-sequence #\, (subseq line 12)))
(setf c-vars (length vars))
(setf data (make-array c-vars)))
...
The first two parts of `cond' will deal with the header, and after
that, I was thinking of having a `t' clause with code that will parse
the successively rad lines and store the contents.
I tried this for line parsing at the repl and got an infinite loop:
(let ((start 0) item)
(do ()
((eq item 'end))
(multiple-value-bind (item start)
(read-from-string "1 2 3 4" nil 'end :start start)
(print start))))
The goal is to reset the `start' keyword in read-from-string using the
second value it returns during the previous read. But, (on sbcl at
least), `start' does not get redefined in the call to `read-from-
string'. I guess the multiple-value-bind evaluates it during
compilation and it is not changed.
So, (although a workaround is described below), I wonder how one would
do this?
As for the workaround, after reading the header, I exit the line-
reading loop, and then revert to using read to read column item by
column item all the way to the end.
Thanks,
Mirko
From: Thomas A. Russ
Subject: Re: read columns from a file - how to use read-from-string?
Date:
Message-ID: <ymik54m47s1.fsf@blackcat.isi.edu>
Mirko <·············@gmail.com> writes:
> I have a slightly different file structure (in tecplot format), where
> the file has two headers that I first have to parse in order to parse
> the columns. So, I have to read lines, and then decide what to do.
> (There is another solution that I discuss later on).
> Here is my code so far:
> (with-open-file (stream file :direction :input
> :if-does-not-exist :error)
> (let (header vars c-vars data)
> (loop for line = (read-line stream nil 'eof)
> until (eq line 'eof)
> do (cond
> ((string= "#" (subseq line 0 1)) (push line header))
> ((string= "VARIABLES" (subseq line 0 9))
> (setf vars (split-sequence #\, (subseq line 12)))
> (setf c-vars (length vars))
> (setf data (make-array c-vars)))
> ...
>
> The first two parts of `cond' will deal with the header, and after
I would use some more specialized functions for doing the matching.
SUBSEQ needlessly creates a new string for doing the comparison.
For the first one I would use something like
(eql #\# (char line 0)) ;; or CHAR=
For the second I would do something like
(null (mismatch "VARIABLES" line :end2 9))
Also, you should do some testing of the length of the line that is being
read, since calling SUBSEQ (or any other accessor) with out-of-bounds
indices will generate an error. You can easily get that information by
adding a
as length = (length line)
iteration clause after the "for" clause in your LOOP.
> that, I was thinking of having a `t' clause with code that will parse
> the successively rad lines and store the contents.
> I tried this for line parsing at the repl and got an infinite loop:
> (let ((start 0) item)
> (do ()
> ((eq item 'end))
> (multiple-value-bind (item start)
> (read-from-string "1 2 3 4" nil 'end :start start)
> (print start))))
>
> The goal is to reset the `start' keyword in read-from-string using the
> second value it returns during the previous read. But, (on sbcl at
> least), `start' does not get redefined in the call to `read-from-
> string'. I guess the multiple-value-bind evaluates it during
> compilation and it is not changed.
Nope. That is not the problem.
The problem is that multiple-value-BIND introduces a NEW binding of the
variables. It works like having another LET form establishing a new set
of variables with that name.
The form you want is multiple-value-SETQ instead. You would discover
that ITEM never changes outside the body of the MULTIPLE-VALUE-BIND
either.
Didactic digression. You could define MULTIPLE-VALUE-BIND as follows:
(defmacro multiple-value-bind ((&rest vars) value-form &body body)
`(let (,@vars)
(multiple-value-setq (,@vars) ,value-form)
,@body))
--
Thomas A. Russ, USC/Information Sciences Institute
From: Peder O. Klingenberg
Subject: Re: read columns from a file - how to use read-from-string?
Date:
Message-ID: <ks8wl25raa.fsf@netfonds.no>
Mirko <·············@gmail.com> writes:
> I tried this for line parsing at the repl and got an infinite loop:
> (let ((start 0) item)
> (do ()
> ((eq item 'end))
> (multiple-value-bind (item start)
> (read-from-string "1 2 3 4" nil 'end :start start)
> (print start))))
The m-v-b creates new bindings, only visible in the body of the m-v-b.
That binding is discarded before the next round in the iteration. So
each round in the iteration, start is 0 and item is nil.
Try this instead:
(let ((start 0) item)
(do ()
((eq item 'end))
(multiple-value-setq (item start)
(read-from-string "1 2 3 4" nil 'end :start start))
(print start)))
This actually updates the value of the outer bindings, which is what
you want.
> The goal is to reset the `start' keyword in read-from-string using the
> second value it returns during the previous read. But, (on sbcl at
> least), `start' does not get redefined in the call to `read-from-
> string'. I guess the multiple-value-bind evaluates it during
> compilation and it is not changed.
Wrong guess. Read up on the difference between binding and
assignment. I think there's a good essay on the subject floating
about (by Ron Garrett?) but I don't have a reference, sorry.
...Peder...
--
Sl�v uten dop.
On 12 maio, 11:28, ·····@news.klingenberg.no (Peder O. Klingenberg)
wrote:
> The m-v-b creates new bindings, only visible in the body of the m-v-b.
> That binding is discarded before the next round in the iteration. So
> each round in the iteration, start is 0 and item is nil.
>
> Try this instead:
>
> (let ((start 0) item)
> (do ()
> ((eq item 'end))
> (multiple-value-setq (item start)
> (read-from-string "1 2 3 4" nil 'end :start start))
> (print start)))
(setf (values item start) (read-from-string "1 2 3 4" nil 'end :start
start)) would do as well.
On May 12, 11:04 am, gugamilare <··········@gmail.com> wrote:
> On 12 maio, 11:28, ·····@news.klingenberg.no (Peder O. Klingenberg)
> wrote:
>
> > The m-v-b creates new bindings, only visible in the body of the m-v-b.
> > That binding is discarded before the next round in the iteration. So
> > each round in the iteration, start is 0 and item is nil.
>
> > Try this instead:
>
> > (let ((start 0) item)
> > (do ()
> > ((eq item 'end))
> > (multiple-value-setq (item start)
> > (read-from-string "1 2 3 4" nil 'end :start start))
> > (print start)))
>
> (setf (values item start) (read-from-string "1 2 3 4" nil 'end :start
> start)) would do as well.
Thanks to both suggestions. I was not aware of either possibility.
Mirko