167 votes

Dans Clojure 1.3, comment lire et écrire un fichier ?

J'aimerais connaître la manière "recommandée" de lire et d'écrire un fichier dans clojure 1.3 .

  1. Comment lire le fichier entier
  2. Comment lire un fichier ligne par ligne
  3. Comment écrire un nouveau fichier
  4. Comment ajouter une ligne à un fichier existant

2 votes

Premier résultat de Google : lethain.com/fr/reading-file-in-clojure

8 votes

Ce résultat date de 2009, certaines choses ont été modifiées dernièrement.

12 votes

En effet. Cette question de StackOverflow est maintenant le premier résultat sur Google.

279voto

Michiel Borkent Points 11503

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

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

(slurp "/tmp/test.txt")

Non recommandé lorsqu'il s'agit d'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)))

El with-open La macro veille à ce que le lecteur soit fermé à la fin du corps. La fonction de lecture convertit une chaîne de caractères (elle peut aussi faire une URL, etc.) en un fichier de type BufferedReader . line-seq délivre un seq. paresseux. La demande de l'élément suivant de la séquence paresseuse entraîne la lecture d'une ligne dans le lecteur.

Notez qu'à partir de Clojure 1.7, vous pouvez également utiliser transducteurs pour la lecture 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 le BufferedWriter est fermé à l'extrémité du corps. Le rédacteur contraint une chaîne de caractères dans un BufferedWriter que vous utilisez via java interop : (.write wrtr "something").

Vous pouvez également utiliser spit le contraire de slurp :

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

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

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

Idem que ci-dessus, mais maintenant avec l'option "append".

Ou encore avec spit le contraire de slurp :

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

PS : Pour être plus explicite sur le fait que vous lisez et écrivez dans un fichier et pas dans autre chose, vous pourriez d'abord créer un objet File et ensuite le contraindre dans un objet BufferedReader ou Rédacteur :

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

La fonction fichier se trouve également dans clojure.java.io.

PS2 : Il est parfois pratique de pouvoir voir quel est le répertoire courant (donc "."). Vous pouvez obtenir le chemin absolu de deux manières :

(System/getProperty "user.dir") 

ou

(-> (java.io.File. ".") .getAbsolutePath)

1 votes

Merci beaucoup pour votre réponse détaillée. Je suis heureux de connaître la méthode recommandée pour l'entrée de fichier (fichier texte) dans la version 1.3. Il semble qu'il y ait eu quelques bibliothèques sur les entrées-sorties de fichiers (clojure.contrb.io, clojure.contrib.duck-streams et quelques exemples utilisant directement le BufferedReader FileInputStream InputStreamReader de Java) qui m'ont rendu plus confus. De plus, il y a peu d'informations sur Clojure 1.3, surtout en japonais (ma langue naturelle) Merci.

0 votes

Salut jolly-san, tnx pour accepter ma réponse ! Pour votre information, clojure.contrib.duck-streams est maintenant déprécié. Cela ajoute peut-être à la confusion.

0 votes

C'est-à-dire, (with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr)) donne IOException Stream closed au lieu d'une collection de lignes. Que faire ? J'obtiens de bons résultats avec la réponse de @satyagraha, cependant.

35voto

Paul Points 3633

Si le fichier tient dans la mémoire, vous pouvez le lire et l'écrire sans problème :

(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 termine pas et écrit le contenu du fichier. Si vous voulez ajouter des données au fichier, vous pouvez faire ce qui suit

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

Pour lire ou écrire un fichier de manière linéaire, il faut utiliser le lecteur et l'écrivain de Java. Ils sont enveloppés dans l'espace de noms 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 de reader/writer est que le fichier reste ouvert (dans les instructions let) dans ces derniers et que la lecture et l'écriture sont mises en mémoire tampon, ce qui est plus efficace lors de lectures/écritures répétées dans un fichier.

Voici de plus amples informations : slurp cracher clojure.java.io Le lecteur tampon de Java L'écrivain de Java

1 votes

Merci Paul. J'ai pu en apprendre davantage grâce à vos codes et à vos commentaires qui sont clairs et ciblés pour répondre à ma question. Merci beaucoup.

0 votes

Merci d'avoir ajouté des informations sur les méthodes de niveau légèrement inférieur qui ne sont pas données dans la (excellente) réponse de Michiel Borkent sur les meilleures méthodes pour les cas typiques.

0 votes

@Mars Merci. En fait, j'ai répondu à cette question en premier, mais la réponse de Michiel est plus structurée et semble être très populaire.

6voto

satyagraha Points 158

En ce qui concerne la question 2, on souhaite parfois que le flux de lignes soit renvoyé comme un objet de première classe. Pour obtenir cela comme une séquence paresseuse, tout en ayant le fichier 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)))

7 votes

Je ne pense pas que la nidification defn est un Clojure idéomatique. Votre read-next-line d'après ce que j'ai compris, est visible en dehors de vos read-lines fonction. Vous avez peut-être utilisé une (let [read-next-line (fn [] ...)) à la place.

0 votes

Je pense que votre réponse serait un peu meilleure si elle retournait la fonction créée, (fermeture sur le lecteur ouvert) plutôt que la liaison globale.

1voto

joshua Points 1631

Voici comment lire l'ensemble du fichier.

Si le fichier se trouve dans le répertoire des ressources, vous pouvez le faire :

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

se souvenir d'exiger/utiliser clojure.java.io .

1voto

Chris Murphy Points 41

Pour lire un fichier ligne par ligne, il n'est plus nécessaire de recourir à l'interopérabilité :

(->> "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 est une information d'en-tête qui peut être supprimée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X