Подтвердить что ты не робот

Загрузка конфигурационного файла в 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. Любая идея, как это сделать?

4b9b3361

Ответ 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 и друзей.

Ответ 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)))))