在Clojure中逐个字符处理文件
我正在用Clojure编写一个函数,它将逐个字符地处理文件。我知道Java的BufferedReader类具有读取一个字符的read()方法,但我对Clojure是新手,不知道如何使用它。目前,我只想逐行打印文件,然后打印每个字符在Clojure中逐个字符处理文件,clojure,Clojure,我正在用Clojure编写一个函数,它将逐个字符地处理文件。我知道Java的BufferedReader类具有读取一个字符的read()方法,但我对Clojure是新手,不知道如何使用它。目前,我只想逐行打印文件,然后打印每个字符 (defn process_file [file_path] (with-open [reader (BufferedReader. (FileReader. file_path))] (let [seq (line-seq reader)]
(defn process_file [file_path]
(with-open [reader (BufferedReader. (FileReader. file_path))]
(let [seq (line-seq reader)]
(doseq [item seq]
(let [words (split item #"\s")]
(println words))))))
给定具有此文本输入的文件:
我们非常感激接受国际捐赠,但我们不能
关于从以下机构收到的捐赠的税务处理的任何声明:
在美国境外。光是美国的法律就让我们的小职员应接不暇
我的输出如下所示:
[International donations are gratefully accepted, but we cannot make]
[any statements concerning tax treatment of donations received from]
[outside the United States. U.S. laws alone swamp our small staff.]
(let [chr (.read rdr)]
(if (>= chr 0)
;do your work here
))
虽然我希望它看起来像:
["international" "donations" "are" .... ]
所以我的问题是,如何将上面的函数转换为逐字读取?甚至,如何让它像我期望的那样工作?另外,如果您能给我提供一些让Clojure代码更好的建议,我们将不胜感激
(with-open [reader (clojure.java.io/reader "path/to/file")] ...
我更喜欢用这种方式在clojure中获得阅读器。而且,通过逐个字符
,您是指像读取
这样的文件访问级别,它允许您控制要读取的字节数
编辑
正如@deterb指出的,让我们检查line seq的源代码
(defn line-seq
"Returns the lines of text from rdr as a lazy sequence of strings.
rdr must implement java.io.BufferedReader."
{:added "1.0"
:static true}
[^java.io.BufferedReader rdr]
(when-let [line (.readLine rdr)]
(cons line (lazy-seq (line-seq rdr)))))
(defn char-seq
[^java.io.Reader rdr]
(let [chr (.read rdr)]
(if (>= chr 0)
(cons chr (lazy-seq (char-seq rdr))))))
我伪造了一个char-seq
(defn line-seq
"Returns the lines of text from rdr as a lazy sequence of strings.
rdr must implement java.io.BufferedReader."
{:added "1.0"
:static true}
[^java.io.BufferedReader rdr]
(when-let [line (.readLine rdr)]
(cons line (lazy-seq (line-seq rdr)))))
(defn char-seq
[^java.io.Reader rdr]
(let [chr (.read rdr)]
(if (>= chr 0)
(cons chr (lazy-seq (char-seq rdr))))))
我知道这个char-seq
将所有字符读入内存[1],但我认为它表明您可以在BufferedReader
上直接调用.read
。因此,您可以这样编写代码:
[International donations are gratefully accepted, but we cannot make]
[any statements concerning tax treatment of donations received from]
[outside the United States. U.S. laws alone swamp our small staff.]
(let [chr (.read rdr)]
(if (>= chr 0)
;do your work here
))
你觉得怎么样
[1] 根据@dimagog的评论,char-seq
由于lazy-seq
我不熟悉Java或read()方法,所以我无法帮助您实现它
(defn line-seq
"Returns the lines of text from rdr as a lazy sequence of strings.
rdr must implement java.io.BufferedReader."
{:added "1.0"
:static true}
[^java.io.BufferedReader rdr]
(when-let [line (.readLine rdr)]
(cons line (lazy-seq (line-seq rdr)))))
(defn char-seq
[^java.io.Reader rdr]
(let [chr (.read rdr)]
(if (>= chr 0)
(cons chr (lazy-seq (char-seq rdr))))))
第一个想法可能是通过使用来简化,它将返回整个文件的一个文本字符串,其中只包含(slurp filename)
。但是,这会得到整个文件,这可能是您不想要的
一旦有了一个包含整个文件文本的字符串,就可以逐个字符地处理任何字符串,只需将其视为一个字符序列。例如:
=> (doseq [c "abcd"]
(prntln c))
a
b
c
d
=> nil
或:
您可以使用map
或reduce
或任何类型的序列操作功能。请注意,在像序列一样操作它之后,它现在将作为序列返回,但您可以轻松地将外部部分包装在(reduce str…
中,以将其返回到末尾的字符串中--显式地:
=> (reduce str (remove #{\c} "abcd"))
=> "abd"
至于您的特定代码的问题,我认为问题在于单词是什么:字符串向量。当您打印每个单词时,您正在打印一个向量。如果最后您将行(println words)
替换为(doseq[w words](println w))
,那么它应该工作得很好
另外,根据您所说的您希望输出看起来像什么(文件中所有不同单词的向量),您不希望只在表达式的底部执行(println w)
,因为这将打印值并返回nil
。您只需要w
。此外,您还需要将doseq
s替换为for
s,以避免返回nil
另外,在改进代码方面,我觉得它总体上很好,但是——这是我在上面建议的所有第一个更改(但不是其他更改,因为我不想明确地将其全部列出)——您可以通过一个有趣的小技巧来缩短它:
(doseq [item seq]
(let [words (split item #"\s")]
(doseq [w words]
(println w))))
;//Could be rewritten as...
(doseq [item s
:let [words (split item #"\s")]
w words]
(println w))
你已经非常接近了——请记住字符串是一个序列<代码>(concat“abc”“def”)
生成序列(\a\b\c\d\e\f)
mapcat
是另一个非常有用的函数,它将延迟地连接将映射fn应用到序列的结果。这意味着mapcat
ing将所有行字符串转换为seq
的结果将是您要查找的延迟字符序列
我是作为(mapcat-seq(line-seq-reader))
这样做的
其他建议:
- 对于创建阅读器,我建议使用
clojure.java.io/reader
函数,而不是直接创建类
- 考虑将读取文件和处理(在本例中为打印)字符串的过程彼此分开。虽然将完整的文件解析保持在
withopen
子句中很重要,但是能够在文件读取代码之外测试实际的处理代码是非常有用的
>P>导航多个(潜在嵌套)序列时,请考虑使用<代码> < <代码> >code>for
在处理嵌套for循环类型案例方面做得很好
(取100(表示[line(重复“abc”)字符(seq line)](prn字符))
使用prn
调试输出。与用户输出(隐藏用户通常不关心的某些细节)相比,它提供了真实的输出
哇,感谢您的详细回复。不幸的是,使用slurp对我来说不起作用,因为我要接收的文件可能太大,无法放入内存。我没有意识到我可以拿一个向量,把它变成那样的序列。再次感谢。是的,在文件访问级别,就像读取一样。一旦我有了一个“reader”变量,调用它的“read()”方法的最佳方法是什么?试着看看line seq是如何做到的-只需在REPL中键入(source line seq)。char seq
由于lazy seq
调用,不会读取内存中的所有字符。同时将(cons-chr…
更改为(cons(char-chr)…
,因为.read
返回int.,我认为(但不确定)该类型提示可以放宽为仅java.io.Reader
。这实际上会创建一个整数序列。解码没有发生。如何在那里注入解码阶段?如果使用prn,输出会是什么样子<