Загрузка конфигурационного файла в clojure в качестве структуры данных
Есть ли функция чтения в clojure для синтаксического анализа структуры данных clojure? Моим вариантом использования является чтение файлов свойств конфигурации, а одно значение для свойства должно быть списком. Я хотел бы написать это как:
file.properties:
property1 = ["value1" "value2"]
и в clojure:
(load-props "file.properties")
и получить карту со значением {property1, [ "value1" "value2" ]
Прямо сейчас, я делаю следующее, с тем же входным файлом "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")
Но я не могу получить способ разобрать результат на вектор clojure. Я в основном ищу что-то вроде файла Erlang: consult/1 function. Любая идея, как это сделать?
Ответы
Ответ 1
java.util.Properties
реализует Map
, поэтому это можно сделать очень легко без ручного анализа файлов свойств:
(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"]}
В частности, файлы свойств сложнее, чем вы думаете (комментарии, экранирование и т.д.) и java.util.Properties
очень хороши при загрузке.
Ответ 2
Если вы хотите прочитать файлы свойств в стиле Java, посмотрите на ответ Dave Ray - хотя свойства файлов имеют множество ограничений.
Если вы используете Clojure 1.5 или новее, я предлагаю вам использовать edn, расширяемую нотацию данных, используемую в Datomic - это в основном данные Clojure структуры без произвольного выполнения кода и возможность добавлять теги для таких вещей, как экземпляры или произвольные типы.
Самый простой способ использовать это через read-string и slurp:
(require 'clojure.edn)
(clojure.edn/read-string (slurp "filename.edn"))
Что это. Обратите внимание, что в read-string читается только одна переменная, поэтому вы должны настроить свою конфигурацию как карту:
{ :property1 ["value1" "value2"] }
Тогда:
(require 'clojure.edn)
(def config (clojure.edn/read-string (slurp "config.edn")))
(println (:property1 config))
возвращает
["value1" "value2"]
Ответ 3
Есть ли функция чтения в clojure для синтаксического анализа структуры данных clojure?
Да. Он называется read
. Вы также можете использовать его для чтения данных конфигурации.
Файл props.clj
, содержащий
{:property1 ["value1" 2]
:property2 {:some "key"}}
можно прочитать следующим образом:
(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))))
При чтении недоверенных источников может быть хорошей идеей превратить *read-eval*
:
(def conf (binding [*read-eval* false]
(with-open [r (io/reader "props.clj")]
(read (PushbackReader. r)))))
Чтобы записать данные конфигурации обратно в файл, вы должны посмотреть на функции печати, такие как pr
и друзей.
Ответ 4
вклад имеет функции для чтения свойств записи,
http://richhickey.github.com/clojure-contrib/java-utils-api.html#clojure.contrib.java-utils/as-properties
Если это для вашего собственного потребления, я бы предложил читать/писать clojure структуры данных, которые вы можете просто распечатать на диск и прочитать их.
Ответ 5
(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)))))