## 輸入/輸出
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` 以了解其它一些函數。