Common lisp LISP文件I/O-提取和转换信息

Common lisp LISP文件I/O-提取和转换信息,common-lisp,Common Lisp,我有一个文件(furniture.lisp),基本上是这样的(有更多的条目): 现在,我想要一个函数(get locations“furniture.lisp”“locations.txt”),它提取三维向量中的对象坐标并将其输出写入文件: (location stove -3.1 -0.9 9) (location drawers-cupboard -3.1 0.1 0) ... 我首先编写一个表达式,逐行读取文件(到目前为止没有参数化): (ql:quickload "split-sequ

我有一个文件(furniture.lisp),基本上是这样的(有更多的条目):

现在,我想要一个函数
(get locations“furniture.lisp”“locations.txt”)
,它提取三维向量中的对象坐标并将其输出写入文件:

(location stove -3.1 -0.9 9)
(location drawers-cupboard -3.1 0.1 0)
...
我首先编写一个表达式,逐行读取文件(到目前为止没有参数化):

(ql:quickload "split-sequence")

(with-open-file (stream "furniture.lisp")
(do ((line (read-line stream nil)
           (read-line stream nil)))
    ((null line))
    (princ (split-sequence::split-sequence #\Space line)) ; Just for demonstration
 ))
但我意识到我没有机会/想法“连接”对象的名称(例如火炉)及其坐标。我需要“(addgv”之后的第二个符号作为名称,变量“单词距离”作为坐标。因此我尝试将文件读入一个大列表:

(defun make-list-from-text (fn)
  (with-open-file (stream fn)
    (loop for line = (read-line stream nil nil)
          while line
          collect
               (split-sequence::split-sequence #\Space line))))
因此,每一行都是一个子列表(我不知道这个子结构是否是一个优势,也许我应该“展平”结果)。现在我被卡住了。此外,我有一种感觉,我的方法有点不雅观

编辑:

我遵循Svante的方法,最终得到了所需的输出!除了创建一个虚拟包,我还必须为包创建虚拟导出(例如
:导出:make-3d-vector
)。此外,
:key#car
不起作用,因为我的列表是一个“混合”列表,由子列表组成(例如
(make-instance…
)和符号(例如,
addgv
)。因此我创建了一个助手函数:

(defun find-helper (list-or-symbol)
    (if (listp list-or-symbol)
       (car list-or-symbol)
        list-or-symbol))

并将
#car
替换为
#find-helper

通常的方法是:

  • 以字符串形式读取整个文件(请参阅)
  • (cl-ppcre:regex替换所有“tf:?“content”)
    ,即替换对包
    tf
    的所有引用,以避免与包相关的错误
  • 在内容周围放置
    ”()
  • 读取它并分配给变量
  • 现在,您已经拥有了结构化数据,可以使用各种列表操作函数来处理这些数据

  • 不幸的是,这将是一个不可移植的解决方案:

    • 处理读卡器错误
    • 采取必要措施解决问题
    • 继续
    例如,在LispWorks中,我可以这样做(只是一个草图):


    它为丢失的包错误调用continue restarts,然后为符号未导出的错误调用continue restarts。重新启动创建包,另一个返回未导出的符号…

    我的想法是创建一个伪
    tf
    包,然后
    读取表单并解析您需要的任何内容。因此像这样的方法(未经测试):


    请确保不要忽略将
    *read eval*
    绑定到
    nil

    我不知道
    addgv
    或其他一些函数做什么,但是…如果文件以
    开头(定义家具(列表
    并以额外的
    结尾),会有帮助吗
    ?然后您可以一次读取所有文件,然后使用您喜欢的任何方式迭代家具--
    映射
    dolist
    等。这将文件读取问题与如何从每个条目解析出您想要的内容的问题区分开来。在这种情况下,我得到错误:`没有包含nam的包e“TF”(我没有软件包,因为我只需要坐标)。此外,我对实现这一点的一般方法感兴趣(例如,如果源文件有“糟糕的语法”或根本没有与LISP兼容的样式)。
    (defun find-helper (list-or-symbol)
        (if (listp list-or-symbol)
           (car list-or-symbol)
            list-or-symbol))
    
    CL-USER 60 > (defun test ()
                   (handler-bind ((conditions:package-not-found-reader
                                   (lambda (c)
                                     (continue c)))
                                  (conditions:simple-reader-error
                                   (lambda (c)
                                     (continue c))))
                         (read-from-string "'(foo27:bar19 bar18:foo44)")))
    TEST
    
    CL-USER 61 > (test)
    (QUOTE (FOO27::BAR19 BAR18::FOO44))
    
    (eval-when (:compile-toplevel :load-toplevel :execute)
      (unless (find-package #:tf)
        (defpackage #:tf)))
    
    (defun extract-location-file ()
      (let ((*read-eval* nil))
        (with-open-file (in "furniture.lisp")
          (with-open-file (out "locations.txt"
                               :direction :output
                               :if-exists :supersede
                               :if-does-not-exist :create)
            (loop :for form := (read in nil)
                  :while form
                  :do (print (extract-location form) out)
                      (terpri)))))
    
    (defun extract-location (form)
      `(location ,(third form)
                 ,@(rest (find 'tf::make-3d-vector
                               (find 'tf::make-pose-stamped
                                     (find 'make-instance
                                           form
                                           :key #'car)
                                     :key #'car)
                               :key #'car))))