2011-10-15 15 views
33

Czy istnieje funkcja odczytu w clojure do analizy struktury danych clojure? Moim przypadkiem użycia jest odczyt plików właściwości konfiguracji, a jedną wartością dla właściwości powinna być lista. Chciałbym móc napisać to jako:ładowanie pliku konfiguracyjnego w clojure jako struktura danych

file.properties:

property1 = ["value1" "value2"] 

oraz w Clojure:

(load-props "file.properties") 

i dostać mapę z wartością {property1 [ "Value1 " "VALUE2"]

teraz, m robię następujące, z tego samego pliku wejściowego "file.properties":

(defn load-props [filename] 
    (let [io (java.io.FileInputStream. filename) 
     prop (java.util.Properties.)] 
    (.load prop io) 
    (into {} prop))) 

;; returns: 
;; {"property1" "[\"valu1\", \"valu2\"]"} 
(load-props "file.properties") 

Ale nie mogę znaleźć sposobu na przeanalizowanie wyniku w wektorze clojure. Zasadniczo szukam czegoś takiego jak plik Erlanga: funkcja konsultacji/1. Masz pomysł, jak to zrobić?

+0

Jonasa Odpowiedź jest również dobrą opcją, jeśli nie jesteś ustawiony na pliku właściwości. –

+0

Odpowiedź Korny'ego jest najlepsza tutaj w 2013 roku. – noahlz

Odpowiedz

31

java.util.Properties realizuje Map więc można to zrobić bardzo łatwo, bez ręcznego parsowania właściwości plików:

(require 'clojure.java.io) 
(defn load-props 
    [file-name] 
    (with-open [^java.io.Reader reader (clojure.java.io/reader file-name)] 
    (let [props (java.util.Properties.)] 
     (.load props reader) 
     (into {} (for [[k v] props] [(keyword k) (read-string v)]))))) 

(load-props "test.properties") 
;=> {:property3 {:foo 100, :bar :test}, :property2 99.9, :property1 ["foo" "bar"]} 

W szczególności pliki właściwości są bardziej skomplikowane niż myślisz (komentarze, znaki ucieczki itp. Itp.) I java.util.Properties jest bardzo dobry w ich ładowaniu.

+1

wydaje się, że zapomniałeś powiązania na '* read-eval *': P –

+2

Trochę uproszczona wersja: (na {} rekwizyty) – Lei

+2

Nie musisz robić tego wszystkiego z małą biblioteką o nazwie propertyid (https: //github.com/michaelklishin/propertied) –

3

contrib posiada funkcje do odczytywania właściwości pisanie,

http://richhickey.github.com/clojure-contrib/java-utils-api.html#clojure.contrib.java-utils/as-properties

Jeśli jest to dla własnej konsumpcji następnie Proponuję czytanie/pisanie struktur danych clojure można po prostu wydrukować je na dysku i je przeczytać.

+0

Nie mam pakietu java-utils contrib zainstalowanego w moim komputerze. Przetestuję to później. – Ahmed

+2

Dokumentacja API Clojure's została przeniesiona na http://clojure.github.com/clojure, a clojure-contrib został wycofany/przeniesiony do oddzielnych bibliotek pod tym samym kontem github. – Rayne

0
(use '[clojure.contrib.duck-streams :only (read-lines)]) 
(import '(java.io StringReader PushbackReader)) 

(defn propline->map [line] ;;property1 = ["value1" "value2"] -> { :property1 ["value1" "value2"] } 
    (let [[key-str value-str] (seq (.split line "=")) 
     key (keyword (.trim key-str)) 
     value (read (PushbackReader. (StringReader. value-str)))] 
     { key value })) 

(defn load-props [filename] 
    (reduce into (map propline->map (read-lines filename)))) 

DEMO

user=> (def prop (load-props "file.properties")) 
#'user/prop 
user=> (prop :property1) 
["value1" "value2"] 
user=> ((prop :property1) 1) 
"value2" 

UPDATE

(defn non-blank? [line] (if (re-find #"\S" line) true false)) 
(defn non-comment? [line] (if (re-find #"^\s*\#" line) false true)) 

(defn load-props [filename] 
    (reduce into (map propline->map (filter #(and (non-blank? %)(non-comment? %)) (read-lines filename))))) 
+0

Wspaniale, to jest dokładnie to, czego potrzebowałem. Wprowadziłem zmiany, aby funkcja propline-> map unikała komentarzy. Zaktualizowana wersja znajduje się poniżej (z propline-> map [line] (niech [[key-str value-str] (klawisz seq (.split line "=")) (słowo kluczowe (.trim key-str)) value (read (PushbackReader. (StringReader. value-str))) komentarz? (.startsWith key-str "#")] (jeśli (nie komentarz?) {wartość klucza}))) ' – Ahmed

+0

Jeśli wykluczysz linia komentarza, że ​​filtr może być lepszy w rekordzie obciążenia. – BLUEPIXY

+0

jeszcze lepiej :). – Ahmed

25

Czy istnieje funkcja odczytu w clojure do analizy struktury danych clojure?

Tak. Nazywa się read. Możesz także użyć go do odczytu danych konfiguracyjnych.

Plik props.clj zawierający

{:property1 ["value1" 2] 
:property2 {:some "key"}} 

można odczytać tak:

(ns somens.core 
    (:require [clojure.java.io :as io]) 
    (:import [java.io PushbackReader])) 

(def conf (with-open [r (io/reader "props.clj")] 
      (read (PushbackReader. r)))) 

Czytając niezaufanych źródeł może to być dobry pomysł, aby włączyć z *read-eval*:

(def conf (binding [*read-eval* false] 
      (with-open [r (io/reader "props.clj")] 
       (read (PushbackReader. r))))) 

Do zapisania danych konfiguracyjnych z powrotem do pliku powinieneś spojrzeć na p funkcje rint, takie jak pr i przyjaciele.

+0

Pamiętaj, że plik nie jest nigdy zamknięty w twoim kodzie. Użyj 'with-open'. –

+0

Naprawiono. Dziękuję Ci. – Jonas

+7

Zauważ, że od czasów clojure 1.5 powinieneś użyć [clojure.edn/read] (http://clojure.github.io/clojure/clojure.edn-api.html#clojure.edn/read), ponieważ read może wykonać dowolny kod, i [edn] (https://github.com/edn-format/edn) ma być bezpieczny i przenośny. – Korny

38

Jeśli chcesz odczytać pliki właściwości w stylu Java, spójrz na odpowiedź Dave'a Ray'a - choć pliki właściwości mają wiele ograniczeń. Jeśli używasz Clojure 1.5 lub nowszego, sugeruję użycie edn, rozszerzalnej notacji danych używanej w Datomic - to w zasadzie struktury danych clojure, bez wykonywania dowolnego kodu i możliwość dodawania znaczników do rzeczy takich jak instancje lub dowolne typy.

Najprostszym sposobem użycia jest poprzez read-string i slurp:

(require 'clojure.edn) 
(clojure.edn/read-string (slurp "filename.edn")) 

To wszystko. Należy pamiętać, że do odczytu ciąg czyta tylko jedną zmienną, więc należy ustawić konfigurację mapy:

{ :property1 ["value1" "value2"] } 

wówczas:

(require 'clojure.edn) 
(def config (clojure.edn/read-string (slurp "config.edn"))) 
(println (:property1 config)) 

powraca

["value1" "value2"] 
Powiązane problemy