【问题标题】:Lisp: How to prompt-read a float?Lisp:如何提示读取浮点数?
【发布时间】:2020-05-20 18:44:18
【问题描述】:

我有一个函数,我同时使用 parse-integer 和 prompt-read。但是,我需要这些整数之一是浮点数。当我将 parse-integer 更改为 parse-float 时,它不再起作用。 这是函数:

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

这是按原样工作的,但我需要第一个整数“重量”作为浮点数。 parse-float 不起作用,我找不到正确的方法来做到这一点。

【问题讨论】:

    标签: lisp common-lisp clisp slime


    【解决方案1】:

    有两种方法可以做到这一点。一种是以某种形式使用read,另一种是使用浮点解析库。

    使用read

    委婉地说,使用read 充满危险。除非您小心,否则您尤其容易受到代码注入攻击。 切勿在您不完全信任输入的代码中使用read,除非您至少在其周围采取了一些安全预防措施。

    也就是说,这里有一个函数,它试图以最低限度的安全方式使用read 来读取带有提示的浮点数。

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

    这至少尝试通过标准化语法和关闭读取时评估来更安全,这就是代码注入攻击的发生方式。

    但是,这可能仍然不是完全安全的。除了可能不安全之外,就允许在系统上执行不受控制的代码而言,使用read 没有严格限制的可读表也不是无副作用的(例如,它可以实习符号),并且可以允许各种可能的拒绝服务攻击,例如通过导致大量内存分配。要解决这些问题,您要么需要做大量工作,将可读表缩减为安全的内容,要么使用仅读取您关心的类型的库。

    使用浮点解析库

    有一个名为parse-float 的库可以为您解决这个问题。该库可通过Quicklisp 获得。假设您安装了 Quicklisp(您应该安装它:如果没有,那么您的第一个问题是安排它是真的),然后使用它就像说 (ql:quickload "parse-float") 一样简单,然后解析浮点数,例如:

    > (parse-float:parse-float "12.2")
    12.2
    4
    

    【讨论】:

    • 举例说明为什么prompt-for-float 可能仍然不安全:readtable 允许对可能分配很多的东西使用简短的语法,例如#500000000000*0,但也可以使用循环结构(有趣的#1A#1=(0 . #1#) 数组) 甚至在读取时按零错误进行 div 5/0。此外,read 可能会实习符号(污染)等。好消息是可以采取对策:程序员需要显着限制 readtable 并捕获错误。
    • @coredump:是的,我已经添加了一些关于这类事情的注释。一个有趣的方法是尝试想出一些东西,它会在读取时引发异常并且在堆栈上留下循环结构,如果你最终进入调试器,这可能会引起娱乐。在某个地方,我写了一个受限阅读的东西,它试图将 readtable 束缚得足够远以保证安全。
    【解决方案2】:
    (let ((weight (progn
                    (format t "Weight: ")
                    (read t))))
      (if (floatp weight) weight 0))
    

    【讨论】:

    • 我想我应该更清楚。我已经学习 Lisp 两个多星期了,所以我很新。我不明白这个。我还需要以与在该函数的其余部分中使用它相同的方式使用该“提示读取”函数。因此,如果您认为我知道该怎么做,那是我的错,但我不知道。
    • 看起来可以正常工作。我想可以从我的教授那里学习它的工作原理。谢谢。
    • 我不知道prompt-read这个函数是怎么工作的,所以我用format来代替。特别是它如何存储输入的值。通常使用全局变量是不好的风格 - 并且很危险 - 这似乎是它正在做的事情。这就是 LET 发挥作用的地方。但是对你来说重要的是把 prompt-read 换成 READ,它可以读取和解析一个浮点数。
    • 请注意,为了提高可移植性,需要调用(finish-output),因为可以缓冲输出流。
    • 不要在任何生产代码中使用它。如果您使用READ 而不对其进行非常仔细的控制,那么有人在提示符下键入可以使Lisp 执行任何操作。这相当于允许 SQL 注入攻击,只是语言标准是关于如何进行攻击的文档。
    猜你喜欢
    • 2015-02-28
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 2021-06-13
    • 2018-02-10
    • 2018-12-05
    相关资源
    最近更新 更多