загрузка файла конфигурации в clojure как структура данных - PullRequest
33 голосов
/ 15 октября 2011

Есть ли в clojure функция чтения для анализа структуры данных clojure?Мой пример использования - чтение файлов свойств конфигурации, и одно значение для свойства должно быть списком.Я хотел бы иметь возможность написать это как:

file.properties:

property1 = ["value1" "value2"]

и в ближайшем будущем:

(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.Я в основном ищу что-то вроде файла Эрланга: функция consult / 1.Есть идеи как это сделать?

Ответы [ 5 ]

40 голосов
/ 21 апреля 2013

Если вы хотите прочитать файлы свойств в стиле java, посмотрите на ответ Дэйва Рэя - хотя файлы свойств имеют много ограничений.

Если вы используете Clojure 1.5 или более позднюю версию, я предлагаю вам использовать edn, расширяемая нотация данных, используемая в Datomic - это в основном закрытые структуры данных без произвольного выполнения кода и возможность добавлять теги для таких вещей, как экземпляры или произвольные типы.

Самый простой способ использовать егочерез 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"]
30 голосов
/ 16 октября 2011

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 очень хорошо загружает их.

25 голосов
/ 16 октября 2011

Есть ли в 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 и друзья.

3 голосов
/ 15 октября 2011

contrib имеет функции для чтения свойств записи,

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

Если это для вашего собственного потребления, то я бы предложил читать / записывать структуры данных clojure, вы можете просто распечатать их на диск и прочитать их.

0 голосов
/ 15 октября 2011
(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))))

ДЕМО

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

ОБНОВЛЕНИЕ

(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)))))
...