Clojure - 将文件路径转换为树

时间:2018-03-27 14:40:15

标签: clojure tree

我是Clojure的新手,我正在努力学习如何使用文件路径在Clojure中创建树。我使用file-seq获取目录下的所有文件,并将它们存储在files中。输入是一个文件路径,如下所示:

resources/data/2012/05/02/low.xml
resources/data/2012/05/01/low.xml

我可以使用以下方法获取文件夹和文件的所有个人名称:

(for [x files] 
 (if (.contains (.getPath x) ".json")
  (for [y (str/split (.getPath x) #"\\")] y)))

这给了我所有文件夹的列表,但后来我不知道如何将它们组合成1个列表来创建树结构。如果任何答案可以解释他们的代码如何工作,以协助学习。这两个输入的所需输出为:

(resources (data (2012 (05 (02 (low.xml)) (01 (low.xml))))))

3 个答案:

答案 0 :(得分:5)

你需要建造树木是这样的:

(defn as-tree [data]
  (map (fn [[k vs]] (cons k (as-tree (keep next vs))))
       (group-by first data)))

给出一个解析路径列表(或者通常是任何序列),它会创建你的结构:

user> (as-tree [["resources" "data" "2012" "05" "02" "low.xml"]
                ["resources" "data" "2012" "05" "01" "aaa.xml"]
                ["resources" "data" "2012" "05" "02" "high.xml"]
                ["resources" "data" "2012" "05" "01" "xsxs.xml"]
                ["resources" "data" "2012" "06" "01" "bbb.xml"]
                ["resources" "data" "2012" "05" "01" "ccc.xml"]
                ["resources" "data" "2012" "02" "some.xml"]
                ["resources" "data" "2012" "01" "some2.xml"]
                ["other-resources" "data" "2015" "10" "some100.xml"]])

;; (("resources" 
;;   ("data" 
;;     ("2012" 
;;       ("05" 
;;         ("02" ("low.xml") 
;;               ("high.xml")) 
;;         ("01" ("aaa.xml") 
;;               ("xsxs.xml") 
;;               ("ccc.xml"))) 
;;       ("06" 
;;         ("01" ("bbb.xml"))) 
;;       ("02" ("some.xml")) 
;;       ("01" ("some2.xml"))))) 
;;  ("other-resources" ("data" ("2015" ("10" ("some100.xml"))))))

所以在你的情况下,它看起来像这样(项目中.clj文件的树):

(require '[clojure.string :as cs])
(import 'java.io.File)

(->> (File. ".")
     file-seq
     (map #(.getPath %))
     (filter #(cs/ends-with? % ".clj"))
     (map #(cs/split % (re-pattern File/separator)))
     as-tree
     first)

;;=> ("." 
;;     ("src" 
;;       ("playground" 
;;         ("core.clj"))) 
;;     ("test" 
;;       ("playground" 
;;         ("core_test.clj"))) 
;;     ("project.clj"))

答案 1 :(得分:1)

使用给定的文件/目录结构:

/tmp/root
├── file1.txt
├── file2.txt
├── sub
│   ├── file5.txt
│   └── file6.txt
└── sub1
    ├── emptysub
    ├── file3.txt
    ├── file4.txt
    └── subsub
        └── file99.txt

这是一种用拉链构建(从空树上)的方法,给出了这些路径:

(def paths
  (for [x (file-seq (io/file "/tmp/root"))]
    (keep not-empty (str/split (.getPath x) #"/"))))

(defn level-loc [loc v] ;; find node with value at same depth as loc
  (loop [l loc]
    (when l
      (let [n (z/node l)]
        (cond
          (= n v) l
          (and (coll? n) (= (first n) v)) (-> l z/down)
          :else (recur (-> l z/right)))))))

(defn graft-path [loc path]
  (reduce
    (fn [[_ path :as loc] p]
      (or (level-loc loc p) ;; find existing node
          (if (nil? path)
            ;; appends at top of tree
            (-> loc
                (z/append-child p)
                z/down)
            ;; inserts at depth
            (-> loc
                (z/insert-right (list p))
                z/right
                z/down))))
    loc
    path))

(defn paths->tree [paths]
  (z/root
    (reduce
      (comp z/seq-zip z/root graft-path)
      (z/seq-zip '())
      paths)))

产生以下输出:

(paths->tree paths)
=>
("tmp"
 ("root"
  ("sub" ("file6.txt") ("file5.txt"))
  ("sub1" ("emptysub") ("subsub" ("file99.txt")) ("file4.txt") ("file3.txt"))
  ("file1.txt")
  ("file2.txt")))

答案 2 :(得分:1)

一种方法是按照此处所述的顺序遍历目录:https://docs.oracle.com/javase/tutorial/essential/io/walk.html

另一种方法是在将路径名拆分为组件字符串后累积结果,然后重复使用plt.axhline(y=-10,linestyle='--',color='red', xmin=0.5)

assoc-in

(ns tst.demo.core (:use demo.core tupelo.core tupelo.test) (:require [clojure.string :as str] )) (defn accum-tree "Accumulates a file path into a map tree" [file-elem-tree path-str] (let [path-elems (str/split path-str #"/") key-seq (butlast path-elems) file-name (last path-elems)] (assoc-in file-elem-tree key-seq file-name))) 的每次调用都是这样的:

accum-tree

单元测试显示最终结果。

path-elems  => ["resources" "data" "2012" "05" "02" "low.xml"]
key-seq     => ("resources" "data" "2012" "05" "02")
file-name   =>  "low.xml"