在从文件中读取时,在clojure中分割线条

时间:2017-11-16 15:39:30

标签: file-io clojure split

我正在学校学习clojure,我的考试即将开始。我只是在做一些事情,以确保我掌握它。

我试图逐行读取文件,而且正如我所做的那样,只要有&#34 ;;"我就想分割行。

到目前为止,这是我的代码

(defn readFile []
  (map (fn [line] (clojure.string/split line #";"))
  (with-open [rdr (reader "C:/Users/Rohil/Documents/work.txt.txt")]
    (doseq [line (line-seq rdr)]
      (clojure.string/split line #";")
        (println line)))))

当我这样做时,我仍然得到输出:

"I;Am;A;String;"

我错过了什么吗?

3 个答案:

答案 0 :(得分:11)

我不确定你是否在学校需要这个,但由于加里已经给出了一个很好的答案,所以认为这是一个奖励。

您可以使用传感器对文本行进行优雅的转换。您需要的成分可以让您将线条视为可简化的集合,并在您完成缩减时关闭阅读器:

(defn lines-reducible [^BufferedReader rdr]
  (reify clojure.lang.IReduceInit
    (reduce [this f init]
      (try
        (loop [state init]
          (if (reduced? state)
            @state
            (if-let [line (.readLine rdr)]
              (recur (f state line))
              state)))
        (finally
          (.close rdr))))))

现在,您可以根据输入work.txt执行以下操作:

I;am;a;string
Next;line;please

计算每个'

的长度
(require '[clojure.string :as str])
(require '[clojure.java.io :as io])

(into []
      (comp
       (mapcat #(str/split % #";"))
       (map count))
      (lines-reducible (io/reader "/tmp/work.txt")))
;;=> [1 2 1 6 4 4 6]

总结所有'

的长度
(transduce
 (comp
  (mapcat #(str/split % #";"))
  (map count))
 +
 (lines-reducible (io/reader "/tmp/work.txt")))
;;=> 24

总结所有单词的长度,直到找到长于5的单词

(transduce
 (comp
  (mapcat #(str/split % #";"))
  (map count))
 (fn
   ([] 0)
   ([sum] sum)
   ([sum l]
    (if (> l 5)
      (reduced sum)
      (+ sum l))))
 (lines-reducible (io/reader "/tmp/work.txt")))

take-while

(transduce
 (comp
  (mapcat #(str/split % #";"))
  (map count)
  (take-while #(> 5 %)))
 +
 (lines-reducible (io/reader "/tmp/work.txt")))

阅读https://tech.grammarly.com/blog/building-etl-pipelines-with-clojure了解详情。

答案 1 :(得分:8)

TL; DR拥抱REPL并拥抱不变性

你的问题是“我错过了什么?”而且我会说你错过了Clojure的最佳功能之一,即REPL。

编辑您可能也会遗漏Clojure使用不可变数据结构

请考虑以下代码段:

(doseq [x [1 2 3]]
   (inc x)
   (prn x))

此代码不会打印“2 3 4”

它打印“1 2 3”,因为x不是可变变量。

在第一次迭代中(inc x)被调用,返回2,并且由于它没有被传递给任何东西而被抛弃,然后(prn x)打印x的值,它仍然是1。

现在考虑一下这段代码:

(doseq [x [1 2 3]] (prn (inc x)))

在第一次迭代期间,inc将其返回值传递给prn,因此得到2

长例:

我不想剥夺你自己解决问题的机会,所以我会用另一个问题作为例子。

给定文件"birds.txt" 数据"1chicken\n 2duck\n 3Larry" 你想编写一个接收文件并返回一系列鸟名的函数

让我们把这个问题分解成更小的块:

首先让我们读取文件并将其拆分为行

(slurp "birds.txt")将为整个文件提供一个字符串

clojure.string/split-lines将为我们提供一个集合,每行作为集合中的元素

(clojure.string/split-lines (slurp "birds.txt"))让我们["1chicken" "2duck" "3Larry"]

此时我们可以在该集合上映射一些函数来去除像(map #(clojure.string/replace % #"\d" "") birds-collection)

这样的数字

或者当整个文件是一个字符串时,我们可以将该步骤向上移动。

既然我们已经拥有了所有的作品,我们可以将它们放在一个功能性的管道中,其中一个部分的结果将输入到下一个

在Clojure中有一个很好的宏来使这个更具可读性,->

它接受一次计算的结果并将其作为第一个参数注入下一个

所以我们的管道看起来像这样:

(-> "C:/birds.txt"
     slurp
     (clojure.string/replace #"\d" "") 
     clojure.string/split-lines)
关于样式的最后一个注释,对于你想要坚持kebab case的Clojure函数,所以readFile应该是read-file

答案 2 :(得分:-1)

我会保持简单,并将其编码为:

(ns tst.demo.core
  (:use tupelo.test)
  (:require [tupelo.core :as t]
            [clojure.string :as str] ))
(def text
 "I;am;a;line;
  This;is;another;one
  Followed;by;this;")

(def tmp-file-name "/tmp/lines.txt")

(dotest
  (spit tmp-file-name text) ; write it to a tmp file
  (let [lines       (str/split-lines (slurp tmp-file-name))
        result      (for [line lines]
                      (for [word (str/split line #";")]
                        (str/trim word)))
        result-flat (flatten result)]
(is= result
  [["I" "am" "a" "line"]
   ["This" "is" "another" "one"]
   ["Followed" "by" "this"]])

请注意result是一个双重嵌套(2D)单词矩阵。撤消此操作的最简单方法是生成flatten的{​​{1}}函数:

result-flat

您也可以使用(is= result-flat ["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"]))) ,如下所示:

apply concat

如果您想首先避免构建2D矩阵,可以通过(is= (apply concat result) result-flat) generator function from the Tupelo library使用lazy-gen(一个Python):

yield

在这种情况下,(dotest (spit tmp-file-name text) ; write it to a tmp file (let [lines (str/split-lines (slurp tmp-file-name)) result (t/lazy-gen (doseq [line lines] (let [words (str/split line #";")] (doseq [word words] (t/yield (str/trim word))))))] (is= result ["I" "am" "a" "line" "This" "is" "another" "one" "Followed" "by" "this"]))) 创建生成器函数。 请注意,lazy-gen已替换为fordoseq函数会将每个单词放入输出延迟序列。