web-dev-qa-db-fra.com

Dans Clojure 1.3, Comment lire et écrire un fichier

J'aimerais connaître la méthode "recommandée" de lecture et d'écriture d'un fichier dans clojure 1.3.

  1. Comment lire tout le fichier
  2. Comment lire un fichier ligne par ligne
  3. Comment écrire un nouveau fichier
  4. Comment ajouter une ligne à un fichier existant
157
jolly-san

En supposant que nous ne faisons que des fichiers texte ici et pas des trucs binaires loufoque.

Numéro 1: comment lire un fichier entier en mémoire.

(Slurp "/tmp/test.txt")

Non recommandé quand c'est un très gros fichier.

Numéro 2: comment lire un fichier ligne par ligne.

(use 'clojure.Java.io)
(with-open [rdr (reader "/tmp/test.txt")]
  (doseq [line (line-seq rdr)]
    (println line)))

La macro with-open veille à ce que le lecteur soit fermé à la fin du corps. La fonction de lecteur convertit une chaîne (elle peut également créer une URL, etc.) dans un BufferedReader. line-seq offre une seq paresseuse. Exiger le prochain élément de la valeur paresseuse donne une ligne lue par le lecteur.

Notez qu'à partir de Clojure 1.7, vous pouvez également utiliser transducers pour lire des fichiers texte.

Numéro 3: comment écrire dans un nouveau fichier.

(use 'clojure.Java.io)
(with-open [wrtr (writer "/tmp/test.txt")]
  (.write wrtr "Line to be written"))

Encore une fois, with-open veille à ce que la BufferedWriter soit fermée à la fin du corps. Writer force une chaîne dans une BufferedWriter, que vous utilisez via Java interop: (.write wrtr "something").

Vous pouvez également utiliser spit, l'inverse de Slurp:

(spit "/tmp/test.txt" "Line to be written")

Numéro 4: ajoute une ligne à un fichier existant.

(use 'clojure.Java.io)
(with-open [wrtr (writer "/tmp/test.txt" :append true)]
  (.write wrtr "Line to be appended"))

Comme ci-dessus, mais maintenant avec l'option d'ajout.

Ou encore avec spit, l'inverse de Slurp:

(spit "/tmp/test.txt" "Line to be written" :append true)

PS: Pour être plus explicite sur le fait que vous lisiez et écriviez dans un fichier et pas autre chose, vous pouvez d'abord créer un objet File, puis le contraindre dans un BufferedReader ou un Writer:

(reader (file "/tmp/test.txt"))
;; or
(writer (file "tmp/test.txt"))

La fonction de fichier est également dans clojure.Java.io.

PS2: Parfois, il est utile de pouvoir voir quel est le répertoire actuel (donc "."). Vous pouvez obtenir le chemin absolu de deux manières:

(System/getProperty "user.dir") 

ou 

(-> (Java.io.File. ".") .getAbsolutePath)
262
Michiel Borkent

Si le fichier tient dans la mémoire, vous pouvez le lire et l'écrire avec Slurp et spit:

(def s (Slurp "filename.txt"))

(s contient maintenant le contenu d'un fichier sous forme de chaîne)

(spit "newfile.txt" s)

Cela crée newfile.txt s'il ne se ferme pas et écrit le contenu du fichier . Si vous souhaitez ajouter au fichier, vous pouvez le faire. 

(spit "filename.txt" s :append true)

Pour lire ou écrire un fichier ligne par ligne, utilisez le lecteur et le rédacteur Java. Ils sont enveloppés dans le namespace clojure.Java.io:

(ns file.test
  (:require [clojure.Java.io :as io]))

(let [wrtr (io/writer "test.txt")]
  (.write wrtr "hello, world!\n")
  (.close wrtr))

(let [wrtr (io/writer "test.txt" :append true)]
  (.write wrtr "hello again!")
  (.close wrtr))

(let [rdr (io/reader "test.txt")]
  (println (.readLine rdr))
  (println (.readLine rdr)))
; "hello, world!"
; "hello again!"

Notez que la différence entre Slurp/spit et les exemples lecteur/graveur réside dans le fait que le fichier reste ouvert (dans les instructions let) et que la lecture et l'écriture sont mises en mémoire tampon, donc plus efficaces lors de lectures/écritures répétées dans un fichier.

Voici plus d'informations: Slurpspitclojure.Java.ioBufferedReader de JavaWriter de Java

31
Paul

Concernant la question 2, on souhaite parfois que le flux de lignes soit renvoyé sous la forme d'un objet de première classe. Pour obtenir cela comme une séquence lazy et que le fichier soit toujours fermé automatiquement sur EOF, j'ai utilisé cette fonction:

(use 'clojure.Java.io)

(defn read-lines [filename]
  (let [rdr (reader filename)]
    (defn read-next-line []
      (if-let [line (.readLine rdr)]
       (cons line (lazy-seq (read-next-line)))
       (.close rdr)))
    (lazy-seq (read-next-line)))
)

(defn echo-file []
  (doseq [line (read-lines "myfile.txt")]
    (println line)))
6
satyagraha

Voici comment lire l’ensemble du fichier.

Si le fichier est dans le répertoire de ressources, vous pouvez faire ceci:

(let [file-content-str (Slurp (clojure.Java.io/resource "public/myfile.txt")])

n'oubliez pas d'exiger/d'utiliser clojure.Java.io 

1
joshua
(require '[clojure.Java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)
0
Dima Fomin

Pour lire un fichier ligne par ligne, vous n'avez plus besoin d'interopérer:

(->> "data.csv"
      io/resource
      io/reader
      line-seq
      (drop 1))

Cela suppose que votre fichier de données est conservé dans le répertoire des ressources et que la première ligne contient des informations d'en-tête pouvant être ignorées. 

0
Chris Murphy