Reputation: 163
I have a function where I am using parse-integer and prompt-read together. However, I need one of these integers to be a float. When I change parse-integer to parse-float it no longer works. Here is the function:
(defun prompt-for-cat ()
(add-record
(make-cat
(prompt-read "Name")
(prompt-read "Coloring")
(or (parse-integer (prompt-read "Weight") :junk-allowed t) 0)
(or (parse-integer (prompt-read "Experience") :junk-allowed t) 0)
(or (parse-integer (prompt-read "Length") :junk-allowed t) 0))))
This works as is, but I need that first integer, "Weight" to be a float. parse-float does not work and I cannot find the correct way to do this.
Upvotes: 0
Views: 594
Reputation:
There are two approaches to doing this. One is to use read
in some form, and the other one is to use a float-parsing library.
read
Using read
is fraught with danger, to put it mildly. In particular you are open to code injection attacks unless you are careful. Never use read
in code where you do not fully trust the input, unless you have, at least, wrapped some safety precautions around it.
That said, here is a function which tries to use read
in a minimally safe way to read a float with a prompt.
(defun prompt-for-float (prompt &optional (default 0.0))
;; Use READ to read a float, in a way which should be at least
;; minimally safe.
(with-standard-io-syntax
(let ((*read-eval* nil))
(format *query-io* "~A: " prompt)
(finish-output *query-io*)
(let ((result (read *query-io*)))
(typecase result
(float result)
(real (coerce result 'float))
(t default))))))
This at least tries to be safer by standardising the syntax, and turning off read-time evaluation, which is how code injection attacks happen.
However this may still not be completely safe. As well as possibly not being safe, in the sense of allowing the execution of uncontrolled code on the system, using read
without a heavily-constrained readtable also is not side-effect free (it can intern symbols, for instance), and can allow various possible denial-of-service attacks, for instance by causing a large amount of memory allocation. To deal with those problems you either need to do a lot of work cutting the readtable down to something which is safe, or use a library which just reads the type or types you care about.
There is a library called parse-float which solves this problem for you. This library is available via Quicklisp. Assuming you have Quicklisp installed (which you should have: if not, then your first problem is arranging for that to be true), then using is it is as simple as saying (ql:quickload "parse-float")
, and then parsing the float with, for instance:
> (parse-float:parse-float "12.2")
12.2
4
Upvotes: 4
Reputation: 1934
(let ((weight (progn
(format t "Weight: ")
(read t))))
(if (floatp weight) weight 0))
Upvotes: 1