Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Clojure中逐个字符处理文件_Clojure - Fatal编程技术网

在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)]

我正在用Clojure编写一个函数,它将逐个字符地处理文件。我知道Java的BufferedReader类具有读取一个字符的read()方法,但我对Clojure是新手,不知道如何使用它。目前,我只想逐行打印文件,然后打印每个字符

(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,输出会是什么样子<