In my last Clojure post, I was trying to demonstrate how to use the with-command-line library. In the process, I built a little line/word count tool as an example and that also led me to explore the awesome duck-streams library that’s available as well. There’s not actually too much code to show because the duck-streams lib makes things really simple.
Here’s a simple line count function:
(defn count-lines [file] (count (read-lines file)))
The read-lines function takes something that can be coerced into a reader (a file name, a url, a Reader, an InputStream, a Socket, etc) and lazily returns lines read from the source. And then we just count them up.
Counting characters is also pretty easy and doesn’t even require duck-streams, just the slurp function in clojure.core:
(defn count-chars [file] (count (slurp file)))
slurp just reads the whole file into a string and returns it. This won’t work for big files of course but was no big deal for this simple demo app. You can also use
slurp* in duck-streams to slurp from a Reader instead of a file.
Those functions took literally 5 minutes to write so I figured I’d take on count-words as well and it’s almost as easy:
(defn count-words [file] (count (.split #"\s+" (slurp file))))
Here I also
slurp the entire file first. I use the
#"" syntax to define a regular expression (really a
java.util.Pattern), here I’m using the built-in character set \s for whitespace and + to indicate one or more spaces.
.split which invokes the Java method but you can also use a slightly prettier and more capable function
split in clojure.contrib.str-utils2.
That’s it! I’m really just scratching the surface of duck-streams though – it has tons of useful stuff in it for all manner of I/O.