November 16, 2009.
While getting started looking into Clojure, it took me a bit of investigation to figure out reading from files, and understanding writing out files required a bit more investigation of its own. Outlined below are a few recipes for writing files in Clojure, first using clojure.contrib.duck-streams, and then relying on java.io.BufferedWriter.
duck-stream.spit is the imagine counterpart to the Clojure core's
For simple operations with small files,
spit is probably your best bet.
user> (require 'clojure.contrib.duck-streams) nil user> (clojure.contrib.duck-streams/spit "output.txt" "test") nil
Which will create the
output.txt file with the contents
There is also the
append-spit variant, which appends the string
to the file.
user> (require 'clojure.contrib.duck-streams) nil user> (clojure.contrib.duck-streams/append-spit "output.txt" "test") nil
These are really quite helpful functions, but keep in mind you'll need to install clojure.contrib before you can take advantage of them.
For streaming writing to files, once again
the simplest approach via write-lines.
user> (require 'clojure.contrib.duck-streams) nil user> (clojure.contrib.duck-streams/write-lines "output.txt" (list 1 2 3 4)) nil
Unfortunately, not everyone will immediately install
clojure-contrib, so let's take
a look at writing to files without
When the Clojure core and contributed libraries let us down (or if we don't want to install the contributed library, in this case) the next place to turn is The Java Way: in this case java.io.BufferedWriter.
At its simplest,
java.io.BufferedWriter can help us duplicate the
(ns tokenize (:import (java.io BufferedWriter FileWriter))) (defn spit2 [file-name data] (with-open [wtr (BufferedWriter. (FileWriter. file-name))] (.write wtr data))) (spit2 "output.txt" "this is some data...\n")
spit2 shares the same flaws as
slurp, requiring the entire file
to be held in memory rather than acting on streams of data. We can use
to write a more efficient approach as well.
(ns tokenize (:import (java.io BufferedWriter FileWriter))) (defn write-lines2 [file-name lines] (with-open [wtr (BufferedWriter. (FileWriter. file-name))] (doseq [line lines] (.write wtr line)))) (write-lines2 "output2.txt" (list "a\n" "b" "c" "d\n" "e\n"))
One of these four techniques (
should be enough for most of your file writing needs. The next scenario to consider is a system which lazily reads from
a file, performs some operation, and then lazily writes the output to a file. Hopefully that'll be ready in a day or two.