2015-02-23 38 views
2

我正在学习clojure并希望将一条记录跨越多行读入一组地图。实际上,该文件的内容是从AWS控制台上的ami映像/快照/卷和实例列表复制并粘贴的。用clojure将多行读入记录

生成的文本文件的内容是这样的: -

Record 1 Field Value 1 
Record 1 Field Value 2 
Record 1 Field Value 3 
Record 2 Field Value 1 
Record 2 Field Value 2 
Record 2 Field Value 3 

我写的是什么

(defn read-file [file] 
    (letfn [(readit [rdr] 
     (lazy-seq 
      (if-let [ami-name (.readLine rdr) ] 
       (cons ami-name (readit rdr)) 
       (do (.close rdr) nil))))] 
     (filter #(not (clojure.string/blank? %)) (readit (clojure.java.io/reader file))))) 

它的伟大工程,它的一切追加到列表中。但我的最终目标是将三个相似的文件读入三组地图,然后将它们结合在一起创建一些有意义的东西,找出具有差异的过时记录。我想我可以设法加入基于共同关键字段的三组记录。问题是我无法弄清楚如何将文本文件读入一组地图。这三个文件的格式是类似的,看起来像这样: -

文件1

*Field Count (N)* 
Field Label 1 
Field Label 2 
.. 
Field Label N 
Record 1 Field Value 1 
Record 1 Field Value 2 
Record 1 Field Value N 
Record 2 Field Value 1 
Record 2 Field Value 2 
.. 
Record 2 Field Value N 

地图的结果列表将看起来是这样的: - 如下

(def instance-list 
    #{{Field Label 1: Record 1 Field Value 1 Field Label 2: Record 1 Field Value 2 Record 1 Field Label N: Field Value N} 
    {Field Label 1: Record 2 Field Value 1 Field Label 2: Record 2 Field Value 2 Record 2 Field Label N: Field Value N} 
    {Field Label 1: Record N Field Value 1 Field Label 2: Record N Field Value 2 Record N Field Label N: Field Value N}}) 

样本数据: -

3 
Name 
Instance id 
volume id 
My own instance 1 
Ins-123456 
Vol-234567 
*Blank line* 
My own instance 2 
Ins-123457 
Vol-234568 
*Blank line* 

我的想法是读取第一行作为字段计数,然后将行分成两个g roups,一个作为标题,然后剩余的数据包括: -

user=> (defn parse-int [s] 
    #_=> (Integer. (re-find #"\d+" s))) 

#'user/parse-int 
user=> (split-at (parse-int (first (read-file "test.txt"))) (rest (read-file "test.txt"))) 

[( “姓名”, “实例ID” “” 卷ID “)(” 我的自己的实例1" “INS-123456”,“卷-234567 “”我自己的实例2“”Ins-123457“”Vol-234568“)]

反正我有把这两个列表变成一组映射吗?

任何人都可以帮忙吗?

+0

无论是'字段标签1'一个字符串,即'“字段标签1“'?你是否希望'记录1字段值1'为**一个** **字符串或**两个**字符串或一个地图,例如'{:Record 1:Field_Value 1}'? – 2015-02-23 13:26:47

+1

我之前的问题是关于所有这些空白的更多信息:这是您拥有的实际数据格式吗?你能发布正确的数据样本吗? – 2015-02-23 13:45:05

+0

编辑该问题以提供样本数据并重新格式化文件框架。 – frankcheong 2015-02-23 14:35:19

回答

1

使用您的初始readfile()函数构建记录序列。我选择keyword -ise字段名和记录ID:

(defn record-seq [file] 
    (let [data  (read-file file) 
     nb-fields (Integer/parseInt (first data)) 
     fields (map #(keyword (str/replace % #"\s+" "-")) 
         (take nb-fields (rest data))) 
     values (filter (complement str/blank?) 
          (drop (inc nb-fields) data)) 
     rec-ids (map #(keyword (str "rec-" %)) 
         (range))] 
    (map #(vector %1 (zipmap fields %2)) 
     rec-ids 
     (partition nb-fields values)))) 

user> (pprint (record-seq "./ami.log")) 
([:rec-0 
    {:volume-id "Vol-23456", 
    :Instance-id "Ins-12345", 
    :Name "My own instance 1"}] 
[:rec-1 
    {:volume-id "Vol-2345", 
    :Instance-id "Ins-123457", 
    :Name "My own instance 2"}] 
[:rec-2 
    {:volume-id "Vol-9876", 
    :Instance-id "Ins-123987", 
    :Name "My own instance 3"}]) 

建设的记录set只是一个

(into #{} (map second (record-seq "./ami.log"))) 
+0

它按预期工作 – frankcheong 2015-02-24 09:07:44

1

这里的只有幸福的情况下的尝试,使得没有尝试检查该文件第一次有预期的结构:

(defn read-file [file] 
    (with-open[rdr (clojure.java.io/reader file)] 
    (let[lines (line-seq rdr) 
     num-fields (Long/valueOf (first lines)) 
     fields (->> lines (drop 1) (take num-fields)) 
     block-size (inc num-fields) 
     records (->> lines 
         (drop block-size) 
         (partition block-size) 
         (map (partial zipmap fields)))] 
     (into #{} records)))) 

;;Returns #{{"volume id" "Vol-2345", "Instance id" "Ins-123457", "Name" "My own instance 2"} 
;;   {"volume id" "Vol-23456", "Instance id" "Ins-12345", "Name" "My own instance 1"}} 

注意使用line-seq做很多与您的readit FN。从行seq有几个基本步骤:

  1. 获取字段的数量。
  2. 取那么多行并将它们存储为字段名称。如果需要,您可以将keyword映射到它们上面,并从String中更改它们的数据类型。
  3. 删除字段规范,然后将行seq划分为与各个记录相对应的“块”。我加1是为了让你的*Blank line* s,但这些都没有使用。
  4. 使用zipmap构建我们想要的地图。这是一个非常有用的函数,它需要一个key和一个seq值并将它们粘在一起成为一张地图。我们总是希望使用相同的键(我们的fields),因此我们可以在将它们映射到值的序列之前,将它们作为参数部分应用zipmap。它不会使用没有相应键的值,这是我们如何摆脱空白行。
  5. 使用into将地图收集到一个集合中。
+0

解决方案非常干净和简短。虽然我的不好,没有。的空白行是2而不是一个,这个解决方案将把这些空行混合起来作为将创建记录的字段。无论如何,thx非常感谢您的帮助。 – frankcheong 2015-02-24 09:08:44