输入/输出

Clojure提供了很少的方法来进行输入/输出的操作。因为我们在Clojure代码里面可以很轻松的使用java里面的I/O操作方法。但是?clojure.java.io 库使得使用java的I/O方法更加简单。

这些预定义的special symbols *in* , *out* 以及 *err* 默认被设定成 stdin, stdout 以及 stderr 。如果要flush *out* ,里面的输出,使用 (flush) 方法,效果和 (.flush *out*) 一样。当然这些symbol的binding是可以改变的。比如你可以把输出重定向到 " my.log "文件里面去。 看下面的例子:

(binding [*out* (java.io.FileWriter. "my.log")]
  ...
  (println "This goes to the file my.log.")
  ...
  (flush))

print 可以打印任何对象的字符串表示到out,并且在两个对象之间加一个空格。

println 函数和 print 类似, 但是它会在最后加一个newline符号。默认的话它还会有一个flush的动作。这个默认动作可以通过把 special symbol *flush-on-newline* 设成 false 来取消掉。

newline 函数写一个newline符号 *out* 流里面去。 在调用 print 函数后面手动调用 newline 和直接调用 println 的效果是一样的。

prprn 是和 printprintln 想对应的一对函数, 但是他们输出的形式可以被 Clojure reader去读取。它们对于把Clojure的对象进行序列化的时候比较有用。默认情况下它们不会打印数据的元数据。可以通过把 special symbol *print-meta* 设置成 true 来调整这个行为。

下面的例子演示了我们提到的四个打印方法。注意使用print和pr输出的字符串的不同之处。

(let [obj1 "foo"
      obj2 {:letter \a :number (Math/PI)}] ; a map
  (println "Output from print:")
  (print obj1 obj2)

  (println "Output from println:")
  (println obj1 obj2)

  (println "Output from pr:")
  (pr obj1 obj2)

  (println "Output from prn:")
  (prn obj1 obj2))

上面代码的输出是这样的:

Output from print:
foo {:letter a, :number 3.141592653589793}Output from println:
foo {:letter a, :number 3.141592653589793}
Output from pr:
"foo" {:letter \a, :number 3.141592653589793}Output from prn:
"foo" {:letter \a, :number 3.141592653589793}

所有上面讨论的几个打印函数都会在它们的参数之间加一个空格。你可以通过 str 函数来预先组装好要打印的字符串来避免这个行为, 看下面例子:

(println "foo" 19) ; -> foo 19
(println (str "foo" 19)) ; -> foo19

print-str , println-str , pr-str 以及 prn-str 函数 print , println , prprn 类似, 只是它们返回一个字符串,而不是把他们打印出来。

printf 函数和 print 类似。但是它接受一个format字符串。 format 函数和 printf , 类似,只是它是返回一个字符串而不是打印出来。

with-out-str 把它的方法体里面的所有输出汇总到一个字符串里面并且返回。

with-open 可以自动关闭所关联的连接(.close)方法, 这对于那种像文件啊,数据库连接啊,比较有用,它有点像C#里面的using语句。

line-seq 接受一个 java.io.BufferedReader 参数,并且返回一个LazySeq, 这个LazySeq包含所有的一行一行由BufferedReader读出的文本。返回一个LazySeq的好处在于,它不用马上读出文件的所有的内容, 这会占用太大的内存。相反, 它只需要在需要使用的时候每次读一行出来即可。

下面的例子演示了 with-openline-seq 的用法。 它读出一个文件里面所有的行, 并且打印出包含某个关键字的那些行。

(use '1)

(defn print-if-contains [line word]
  (when (.contains line word) (println line)))

(let [file "story.txt"
      word "fur"]

  ; with-open will close the reader after
  ; evaluating all the expressions in its body.
  (with-open [rdr (reader file)]
    (doseq [line (line-seq rdr)] (print-if-contains line word))))

slurp 函数把一个文件里面的所有的内容读进一个字符串里面并且返回。 spit 把一个字符串写进一个文件里面然后关闭这个文件。

这篇文章只是大概过了一下clojure的io里面提供了哪些函数来进行I/O操作。大家可以看下clojure源文件: clojure/java/io.clj 以了解其它一些函数。