## 输入/输出
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` 的效果是一样的。
`pr` 与 `prn` 是和 `print` 与 `println` 想对应的一对函数, 但是他们输出的形式可以被 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` , `pr` 跟 `prn` 类似, 只是它们返回一个字符串,而不是把他们打印出来。
`printf` 函数和 `print` 类似。但是它接受一个format字符串。 `format` 函数和 `printf` , 类似,只是它是返回一个字符串而不是打印出来。
宏 `with-out-str` 把它的方法体里面的所有输出汇总到一个字符串里面并且返回。
`with-open` 可以自动关闭所关联的连接(.close)方法, 这对于那种像文件啊,数据库连接啊,比较有用,它有点像C#里面的using语句。
`line-seq` 接受一个 `java.io.BufferedReader` 参数,并且返回一个LazySeq, 这个LazySeq包含所有的一行一行由BufferedReader读出的文本。返回一个LazySeq的好处在于,它不用马上读出文件的所有的内容, 这会占用太大的内存。相反, 它只需要在需要使用的时候每次读一行出来即可。
下面的例子演示了 `with-open` 和 `line-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` 以了解其它一些函数。