Учитывая некоторые примеры данных:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[tupelo.string :as ts]
[tupelo.parse :as parse]))
(def data-str "
fred123 1 2 3
fred456 4 5 6
wilma12 1.2
wilma34 3.4
barney1 1
barney2 2
")
Затем вы можете определить функции разбора для каждого типа данных:
(defn fred-parser
[line]
(let [tokens (str/split line #"\p{Blank}+")
root (first tokens)
details (rest tokens)
parsed-root (re-find #"fred\n*" root)
parsed-params (mapv parse/parse-long details)
result {:root parsed-root :params parsed-params}]
result))
(defn wilma-parser
[line]
(let [tokens (str/split line #"\p{Blank}+")
root (first tokens)
details (rest tokens)
parsed-root (re-find #"wilma\n*" root)
parsed-params (mapv parse/parse-double details)
result {:root parsed-root :params parsed-params}]
result))
Я бы сделал карту из шаблона в функцию разбора:
(def pattern->parser
{#"fred\d*" fred-parser
#"wilma\d*" wilma-parser
})
и некоторые функции для поиска правильного парсера для каждой строки (очищенных) данных:
(defn parse-line
[line]
(let [patterns (keys pattern->parser)
patterns-matching (filterv ; keep pattern if matches
(fn [pat]
(ts/contains-match? line pat))
patterns)
num-matches (count patterns-matching)]
(cond
(< 1 num-matches) (throw (ex-info "Too many matching patterns!" {:line line :num-matches num-matches}))
(zero? num-matches) (prn :no-match-found line)
:else (let [parser (get pattern->parser (only patterns-matching))
parsed-line (parser line)]
parsed-line))))
(defn parse-file
[data]
(let
[lines (filterv #(not (str/blank? %)) ; remove blank lines
(mapv str/trim ; remove leading/trailing whitespace
(str/split-lines data))) ; split into lines
parsed-data (mapv parse-line lines)]
parsed-data))
и модульный тест, чтобы показать его в действии:
(dotest
(is= (parse-file data-str)
[{:root "fred", :params [1 2 3]}
{:root "fred", :params [4 5 6]}
{:root "wilma", :params [1.2]}
{:root "wilma", :params [3.4]}
nil
nil])
)
Обратите внимание, что несопоставленные строки возвращают ноль. Вы захотите либо сгенерировать исключение для проблем, либо хотя бы отфильтровать значения nil
. Прямо сейчас вы получите сообщение об ошибке:
-------------------------------
Clojure 1.10.1 Java 14
-------------------------------
Testing tst.demo.core
:no-match-found "barney1 1"
:no-match-found "barney2 2"
Ran 2 tests containing 1 assertions.
0 failures, 0 errors.
Дополнительная документация здесь и здесь .