Files

Writing Text File Content With spit

In a previous post we learned how to read text file contents with the slurp function. To write text file content we use the spit function. We content is defined in the second argument of the function. The first argument allows several types that will turn into a Writer object used for writing the content to. For example a string argument is used as URI and if that is not valid as a file name of the file to read. A File instance can be used directly as argument as well. But also Writer, BufferedWriter, OutputStream, URI, URL and Socket. As an option we can specify the encoding used to write the file content using the :encoding keyword. The default encoding is UTF-8 if we don't specify the encoding option. With the option :append we can define if content needs to be appended to an existing file or the content should overwrite existing content in the file.

In the following example we use the spit function with several types for the first argument:

(ns mrhaki.sample.spit
  (:import (java.io FileWriter FilterWriter StringWriter File)))

;; With spit we can write string content to a file.
;; spit treats the first argument as file name if it is a string.
(spit "files/data/output.txt" (println-str "Clojure rocks!"))

;; We can add the option :append if we want to add text
;; to a file, instead of overwriting the content of a file.
(spit "files/data/output.txt" (println-str "And makes the JVM functional.") :append true)

;; Another option is the :encoding option which is UTF-8 by default
(spit "files/data/output.txt" (str "Clojure rocks!") :encoding "UTF-8")

;; The first argument can also be a File.
(spit (File. "files/data/file.xt") "Sample")

;; We can pass a writer we create ourselves as well.
(spit (FileWriter. "files/data/output.txt" true) "Clojure rocks")

;; Or use a URL or URI instance.
(spit (new java.net.URL "file:files/data/url.txt") "So many options...")
(spit (java.net.URI/create "file:files/data/url.txt") "And they all work!" :append true)

Written with Clojure 1.11.1.

Original post written on October 1, 2022

Reading Text File Content With slurp

The slurp funtion in Clojure can be used to read the contents of a file and return it as a string value. We can use several types as argument for the function. For example a string argument is used as URI and if that is not valid as a file name of the file to read. A File instance can be used directly as argument as well. But also Reader, BufferedReader, InputStream, URI, URL, Socket, byte[] and char[]. As an option we can specify the encoding used to read the file content using the :encoding keyword. The default encoding is UTF-8 if we don't specify the encoding option.

In the following example we use the slurp function in different use cases. We use a file named README with the content Clojure rocks!:

(ns mrhaki.sample.slurp
    (:require [clojure.java.io :as io]
              [clojure.test :refer [is]])
    (:import (java.io File)))
  
  ;; slurp interperts a String value as a file name.
  (is (= "Clojure rocks!" (slurp "files/README")))
  ;; Using the encoding option.
  (is (= "Clojure rocks!" (slurp "files/README" :encoding "UTF-8")))

  ;; We can also use an explicit File object.
  (is (= "Clojure rocks!" (slurp (io/file "files/README"))))
  (is (= "Clojure rocks!" (slurp (File. "files/README"))))
  
  ;; We can also use an URL as argument.
  ;; For example to read from the classpath:
  (is (= "Clojure rocks!" (slurp (io/resource "data/README"))))
  
  ;; Or HTTP endpoint
  (is (= "Clojure rocks!" (slurp "https://www.mrhaki.com/clojure.txt")))

Written with Clojure 1.11.1.

Original post written on September 27, 2022

Create All Parent Directories For A File

The Clojure namespace clojure.java.io contains useful functions to work with files. One of those functions is make-parents. We can pass a java.io.File instance as arguments or use the same arguments that can be passed to the file function that is also in this namespace. The function will create all parent directories for the file. The return result is true if the directories are created (they didn't exist before) and false when the directories didn't have to be created (already exist).

In the following example we see an example of usage of the make-parents function:

(ns mrhaki.io.make-parents
  (:require [clojure.java.io :refer [make-parents file]]
            [clojure.test :refer [is]]))

;; make-parents will create the parents directories for a file 
;; The function returns true if the directories are created, 
;; false if the directories already exist.
(let [file (file "tmp" "clojure" "sample.txt")]
  (is (true? (make-parents file)) "All parent directories are created")
  (is (false? (make-parents file)) "Second time the directory already exists"))

;; make-parents uses the same arguments as the clojure.java.io.file function
(is (true? (make-parents "tmp" "clj" "sample.txt")) "Directories tmp/clj are created")

Written with Clojure 1.10.3.

Original post written on September 27, 2021