【问题标题】:Parsing lines of a file with mixed types in Clojure在 Clojure 中解析具有混合类型的文件的行
【发布时间】:2014-05-26 20:47:40
【问题描述】:

假设我有一个文件,其行看起来像这样apple,pie,100,1000。从每一行我想有一个像["apple" "pie" 100 1000] 这样的向量,其中前两个是字符串,第二个是整数或 Longs 或其他。

解决办法是

(defn foo [line]
  (let [[a b c d] (split line #",")]
    [a b (Integer/parseInt c) (Integer/parseInt d)]))

这可以满足我的要求,但我不确定是否有更好(或更惯用的)方法来做到这一点。

从这个问题What's the easiest way to parse numbers in clojure?,我发现了clojure.edn,所以我可以像这样使用read-line

(map read-string (clojure.string/split "apple,pie,12,134" #","))
;=> (:apple :pie 12 134)

但这给了我符号而不是字符串。

这道题Convert a sequence of strings to integers (Clojure)也类似,只是行中只包含数字,不包含数字和字符串。

注意:clojure-csv (https://github.com/davidsantiago/clojure-csv) 和 clojure.data.csv (https://github.com/clojure/data.csv) 似乎没有自动执行此操作的函数。

【问题讨论】:

  • “更好”取决于您想对数据做什么。您可以使用 name 将符号转换为字符串,或者您可以使用 csv 库、拥有架构等。也许您的问题需要更多上下文。
  • 当您说架构时,您指的是github.com/Prismatic/schema
  • 更好,我的意思是更惯用。也许在某个 clojure 命名空间或第 3 方库中有些东西,但我找不到。

标签: parsing clojure


【解决方案1】:

您可以使用 data.csv 解析您的数据,并使用 Prismatic 的 schema 将字段值强制转换为所需的类型:

;; [org.clojure/data.csv "0.1.2"]
;; [prismatic/schema "0.2.2"]

(require '[schema.core :as s]
         '[schema.coerce :as coerce]
         '[clojure.data.csv :as csv])

(def field-schemas [s/Str s/Str s/Int s/Int])
(def field-coercers
  (mapv coerce/coercer
    field-schemas
    (repeat coerce/string-coercion-matcher)))

(defn coerce-fields [fields]
  (mapv #(%1 %2) field-coercers fields))

最后:

(map coerce-fields (csv/read-csv "apple,pie,12,134"))
;= (["apple" "pie" 12 134])

【讨论】:

  • 你能描述一下它在每个步骤中的作用吗?
  • @leontalbot 当然。 Prismatic 的模式具有值可以匹配或不匹配的“模式”概念和知道如何将不匹配模式的某些类型的值转换为匹配的值的“强制器”概念,例如通过解析数字的字符串表示或从字符串创建关键字。开箱即用不支持异构序列/向量的 AFAICT 模式,因此我选择了向量模式方法——field-schemas。 (为异构序列引入适当的模式类型会稍微复杂一些,尽管不是特别困难 --
  • 我只是不确定这个简单的用例是否会是一个巨大的胜利。)field-coercers 是一个伴随向量; string-coercion-matcher 是一个内置模式,它知道如何执行最常见的强制转换。 coerce-fields 采用 seqable 值并使用字段强制器将值强制为 field-schemas 中指定的类型。
  • 它通过映射#(%1 %2)——一个将其第一个参数(可能本身是一个函数)应用到它的第二个参数的函数——跨越field-coercersfields来实现这一点;因此,将使用field-coercers 中匹配位置的强制器强制一个字段。最后,read-csv 将 CSV 解析为代表各个记录的向量序列;每个这样的向量都是coerce-fields 所期望的形状。这没有做的是验证这些向量的长度;不过,这很容易添加。
  • 你可能是对的,因为这个简单的案例并不值得,但这样的东西正是我想要的。
【解决方案2】:

我认为有一个库可以让这变得非常容易。不记得它的名字,但它的工作原理是这样的

(let [structure [str str int int]]
 (map #(% %2) 
      structure
      (map read-string (clojure.string/split "apple,pie,12,134" #",")))
;=> ("apple" "pie" 12 134)

【讨论】:

  • 这样的东西会很棒。我得找找看能不能找到。
【解决方案3】:

不是很优雅,但是

(map numberise (clojure.string/split "apple,pie,12,134" #","))
; ("apple" "pie" 12 134)

在哪里

(defn numberise [s]
  (try (Integer/parseInt s)
       (catch NumberFormatException nfe s)))

【讨论】:

  • @Sandy 很少使用,据我所见,但我只是一个修补匠。流行的习惯用法似乎是从无法产生实质性结果的函数中返回nil。所以try - catch 不在游戏中。例如,({1 :a} 0) 返回nil(几个反例是([] 3)(int "123"),它们都抛出异常)。如上所述,我认为 Java 互操作是发生异常的主要区域,因此使用了 try - catchtry - catch 在 Clojure 中的合法用途是什么 对我来说听起来是一个合理的问题。
猜你喜欢
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
  • 2021-03-02
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 2018-08-17
  • 1970-01-01
相关资源
最近更新 更多