2014-10-02 25 views
0

我需要一個函數,當給定一個基礎目錄和另一個路徑時,我已經完成了一個簡化版本,它只匹配絕對路徑,但也希望能夠智能地處理'..'和'。'。在路徑中。我不知道最好的方法是什麼功能,給clojure相對路徑?

一些例子:

(relative-path "example" "example/hello") => "hello" 

(relative-path "example" "../example/hello") => "hello" 

(relative-path "example" "/usr/local") => "../../../usr/local" 
+4

我發現很難遵循你的例子。你能否更具體地滿足你的要求? – Jeremy 2014-10-03 01:07:52

+1

請嘗試http://docs.oracle.com/javase/tutorial/essential/io/pathOps.html#relativize – edbond 2014-10-03 11:24:10

回答

0

我想通了一個有點試驗和錯誤後:

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

(defn interpret-dots 
    ([v] (interpret-dots v [])) 
    ([v output] 
    (if-let [s (first v)] 
     (condp = s 
     "." (recur (next v) output) 
     ".." (recur (next v) (pop output)) 
     (recur (next v) (conj output s))) 
     output))) 

(defn drop-while-matching [u v] 
    (cond (or (empty? u) (empty? v)) [u v] 

     (= (first u) (first v)) 
     (recur (rest u) (rest v)) 

     :else [u v])) 

(defn path-vector [path] 
    (string/split (.getAbsolutePath (io/file path)) 
       (re-pattern (System/getProperty "file.separator")))) 

(defn relative-path [root other] 
    (let [[base rel] (drop-while-matching (interpret-dots (path-vector root)) 
             (interpret-dots (path-vector other)))] 
    (if (and (empty? base) (empty? rel)) 
     "." 
     (->> (-> (count base) 
       (repeat "..") 
       (concat rel)) 
      (string/join (System/getProperty "file.separator"))))) 

用法:

(relative-path "example/repack.advance/resources" 
      "example/repack.advance/resources/eueueeueu/oeuoeu") 
;;=> "eueueeueu/oeuoeu" 

(relative-path "example/repack.advance/resources" 
      "/usr/local") 
;;=> "../../../../../../../../usr/local"