Parsing 如何使用Clojure读取/解析以下文本?

Parsing 如何使用Clojure读取/解析以下文本?,parsing,clojure,Parsing,Clojure,文本的结构是这样的 Tag001 0.1, 0.2, 0.3, 0.4 0.5, 0.6, 0.7, 0.8 ... Tag002 1.1, 1.2, 1.3, 1.4 1.5, 1.6, 1.7, 1.8 ... 文件可以有任意数量的TagXXX内容,每个标记可以有任意数量的CSV值行 ==购买力平价。(对不起,这些东西:-) 更多的改进;现在,我的atom笔记本电脑上的31842行数据需要1秒钟左右的时间,比原始代码快7倍。然而,C版本的速度是这个的20倍 (defn add-

文本的结构是这样的

Tag001
 0.1, 0.2, 0.3, 0.4
 0.5, 0.6, 0.7, 0.8
 ...
Tag002
 1.1, 1.2, 1.3, 1.4
 1.5, 1.6, 1.7, 1.8
 ...
文件可以有任意数量的TagXXX内容,每个标记可以有任意数量的CSV值行

==购买力平价。(对不起,这些东西:-)

更多的改进;现在,我的atom笔记本电脑上的31842行数据需要1秒钟左右的时间,比原始代码快7倍。然而,C版本的速度是这个的20倍

(defn add-parsed-code [accu code]
  (if (empty? code)
    accu
    (conj accu code)))

(defn add-values [code comps]
  (let [values comps
        old-values (:values code)
        new-values (if old-values
                     (conj old-values values)
                     [values])]
    (assoc code :values new-values)))

(defn read-line-components [file]
  (map (fn [line] (clojure.string/split line #","))
       (with-open [rdr (clojure.java.io/reader file)]
         (doall (line-seq rdr)))))

(defn parse-file [file]
  (let [line-comps (read-line-components file)]
    (loop [line-comps line-comps
           accu []
           curr {}]
      (if line-comps
        (let [comps (first line-comps)]
          (if (= (count comps) 1) ;; code line?
            (recur (next line-comps)
                   (add-parsed-code accu curr)
                   {:code (first comps)})
            (recur (next line-comps)
                   accu
                   (add-values curr comps))))
        (add-parsed-code accu curr)))))
==PPS

虽然我不明白为什么第一个比第二个快10倍,而不是 slurp、map和with open确实能加快阅读速度;尽管整个阅读/处理时间 没有减少(从7秒减少到6秒)

==PS。
斯库罗的解决方案确实有效。但是解析速度不是很快,所以我必须使用基于C的解析器(它在1~3秒内读取400个文件,而clojure确实需要1~4秒读取单个文件;是的,文件大小相当大)来读取和构造DB,clojure仅用于统计分析部分。

以下解析上述文件,并保持所有值的行分隔。如果这不是您想要的,您可以更改
添加值
功能。解析状态保存在
curr
变量中,而
accu
保存以前解析的标记(即找到“TagXXX”之前出现的所有行)。它允许不带标记的值:

更新:副作用现在封装在专用的
加载文件
功能中

(defn tag? [line]
  (re-matches #"Tag[0-9]*" line))

; potentially unsafe, you might want to change this:
(defn parse-values [line]
  (read-string (str "[" line "]")))

(defn add-parsed-tag [accu tag]
  (if (empty? tag)
      accu
      (conj accu tag)))

(defn add-values [tag line]
  (let [values (parse-values line)
        old-values (:values tag)
        new-values (if old-values
                       (conj old-values values)
                       [values])]
    (assoc tag :values new-values)))

(defn load-file [path]
  (slurp path))

(defn parse-file [file]
  (let [lines (clojure.string/split-lines file)]
    (loop [lines lines ; remaining lines 
           accu []     ; already parsed tags
           curr {}]    ; current tag being parsed
          (if lines
              (let [line (first lines)]
                (if (tag? line)
                    ; we recur after starting a new tag
                    ; if curr is empty we don't add it to the accu (e.g. first iteration)
                    (recur (next lines)
                           (add-parsed-tag accu curr)
                           {:tag line})
                    ; we're parsing values for a currentl tag
                    (recur (next lines)
                           accu
                           (add-values curr line))))
              ; if we were parsing a tag, we need to add it to the final result
              (add-parsed-tag accu curr)))))
我对上面的代码不太感兴趣,但它确实起到了作用。给定如下文件:

Tag001
 0.1, 0.2, 0.3, 0.4
 0.5, 0.6, 0.7, 0.8
Tag002
 1.1, 1.2, 1.3, 1.4
 1.5, 1.6, 1.7, 1.8
Tag003
 1.1, 1.2, 1.3, 1.4
 1.1, 1.2, 1.3, 1.4
 1.5, 1.6, 1.7, 1.8
 1.5, 1.6, 1.7, 1.8
它产生以下结果:

user=> (clojure.pprint/print-table [:tag :values] (parse-file (load-file "tags.txt")))
================================================================
:tag   | :values
================================================================
Tag001 | [[0.1 0.2 0.3 0.4] [0.5 0.6 0.7 0.8]]
Tag002 | [[1.1 1.2 1.3 1.4] [1.5 1.6 1.7 1.8]]
Tag003 | [[1.1 1.2 1.3 1.4] [1.1 1.2 1.3 1.4] [1.5 1.6 1.7 1.8] [1.5 1.6 1.7 1.8]]
================================================================

下面将解析上面的文件,并将所有值分隔开。如果这不是您想要的,您可以更改
添加值
功能。解析状态保存在
curr
变量中,而
accu
保存以前解析的标记(即找到“TagXXX”之前出现的所有行)。它允许不带标记的值:

更新:副作用现在封装在专用的
加载文件
功能中

(defn tag? [line]
  (re-matches #"Tag[0-9]*" line))

; potentially unsafe, you might want to change this:
(defn parse-values [line]
  (read-string (str "[" line "]")))

(defn add-parsed-tag [accu tag]
  (if (empty? tag)
      accu
      (conj accu tag)))

(defn add-values [tag line]
  (let [values (parse-values line)
        old-values (:values tag)
        new-values (if old-values
                       (conj old-values values)
                       [values])]
    (assoc tag :values new-values)))

(defn load-file [path]
  (slurp path))

(defn parse-file [file]
  (let [lines (clojure.string/split-lines file)]
    (loop [lines lines ; remaining lines 
           accu []     ; already parsed tags
           curr {}]    ; current tag being parsed
          (if lines
              (let [line (first lines)]
                (if (tag? line)
                    ; we recur after starting a new tag
                    ; if curr is empty we don't add it to the accu (e.g. first iteration)
                    (recur (next lines)
                           (add-parsed-tag accu curr)
                           {:tag line})
                    ; we're parsing values for a currentl tag
                    (recur (next lines)
                           accu
                           (add-values curr line))))
              ; if we were parsing a tag, we need to add it to the final result
              (add-parsed-tag accu curr)))))
我对上面的代码不太感兴趣,但它确实起到了作用。给定如下文件:

Tag001
 0.1, 0.2, 0.3, 0.4
 0.5, 0.6, 0.7, 0.8
Tag002
 1.1, 1.2, 1.3, 1.4
 1.5, 1.6, 1.7, 1.8
Tag003
 1.1, 1.2, 1.3, 1.4
 1.1, 1.2, 1.3, 1.4
 1.5, 1.6, 1.7, 1.8
 1.5, 1.6, 1.7, 1.8
它产生以下结果:

user=> (clojure.pprint/print-table [:tag :values] (parse-file (load-file "tags.txt")))
================================================================
:tag   | :values
================================================================
Tag001 | [[0.1 0.2 0.3 0.4] [0.5 0.6 0.7 0.8]]
Tag002 | [[1.1 1.2 1.3 1.4] [1.5 1.6 1.7 1.8]]
Tag003 | [[1.1 1.2 1.3 1.4] [1.1 1.2 1.3 1.4] [1.5 1.6 1.7 1.8] [1.5 1.6 1.7 1.8]]
================================================================

这可以使用按函数划分来完成。读起来可能有点晦涩,但可读性很容易提高。此功能在我的迷你mac电脑上执行约500毫秒

首先,我使用以下函数创建了测试数据

(defn write-data[fname]
   (with-open [wrtr (clojure.java.io/writer fname) ]
     (dorun 
        (for [ x (take 7500 (range)) ]
          (do
             (.write wrtr (format "Tag%010d" x))
             (.write wrtr "
                            1.1, 1.2, 1.3, 1.4
                            1.1, 1.2, 1.3, 1.4
                            1.5, 1.6, 1.7, 1.8
                            1.5, 1.6, 1.7, 1.8
                           " ))))))

(write-data "my-data.txt")

; "a b c d " will be converted to [ a b c d ]
(defn to-vec[st]
   (load-string (str "[" st "]")))


(defn my-transform[fname]
   (let [tag (atom {:tag nil})]
      (with-open [rdr (clojure.java.io/reader fname)]
         (doall 
           (into {} 
               (map 
                  (fn[xs] {(first xs) (map to-vec (rest xs))}) 
                     ( partition-by 
                          (fn[y] 
                             (if(.startsWith 
                                  (str y) "Tag") 
                                  (swap! tag assoc :tag y) @tag)) 
                       (line-seq rdr))))))))


(time (count (my-transform "my-data.txt")))
;Elapsed time: 517.23 msecs

这可以使用按函数划分来完成。读起来可能有点晦涩,但可读性很容易提高。此功能在我的迷你mac电脑上执行约500毫秒

首先,我使用以下函数创建了测试数据

(defn write-data[fname]
   (with-open [wrtr (clojure.java.io/writer fname) ]
     (dorun 
        (for [ x (take 7500 (range)) ]
          (do
             (.write wrtr (format "Tag%010d" x))
             (.write wrtr "
                            1.1, 1.2, 1.3, 1.4
                            1.1, 1.2, 1.3, 1.4
                            1.5, 1.6, 1.7, 1.8
                            1.5, 1.6, 1.7, 1.8
                           " ))))))

(write-data "my-data.txt")

; "a b c d " will be converted to [ a b c d ]
(defn to-vec[st]
   (load-string (str "[" st "]")))


(defn my-transform[fname]
   (let [tag (atom {:tag nil})]
      (with-open [rdr (clojure.java.io/reader fname)]
         (doall 
           (into {} 
               (map 
                  (fn[xs] {(first xs) (map to-vec (rest xs))}) 
                     ( partition-by 
                          (fn[y] 
                             (if(.startsWith 
                                  (str y) "Tag") 
                                  (swap! tag assoc :tag y) @tag)) 
                       (line-seq rdr))))))))


(time (count (my-transform "my-data.txt")))
;Elapsed time: 517.23 msecs

也许
REGEX
是合适的。你试过什么吗?也许
REGEX
是合适的。你试过什么吗?哇,这正是我想做的!非常感谢。我想知道是否有没有不带“state”的方法,但我找不到。在上面的代码中,“state”几乎有点“不稳定”:除了在
let
绑定中加载文件外,其他一切都是作为纯函数实现的。每个循环迭代都是从新的值开始的。我更新了代码以清楚地说明上面的一点:除了
加载文件
,所有其他的现在都是纯函数。函数
加载文件
中有一个输入错误:参数名为
路径
,但在下一行
slurp文件
中称为
文件,这正是我想做的!非常感谢。我想知道是否有没有不带“state”的方法,但我找不到。在上面的代码中,“state”几乎有点“不稳定”:除了在
let
绑定中加载文件外,其他一切都是作为纯函数实现的。每个循环迭代都是从新的值开始的。我更新了代码以清楚地说明上面的一点:除了
加载文件
,所有其他的现在都是纯函数。函数
加载文件
:参数名为
路径
,但在下一行
slurp文件
中称为
文件。太棒了!但“标签”部分可能无法通过“标签”区分;这只是一行中的一个词,唯一的线索是这行只有一个词。太好了!但“标签”部分可能无法通过“标签”区分;这只是一行中的一个词,唯一的线索是这行只有一个词。