La with-openmacro se encarga de que el lector esté cerrado al final del cuerpo. La función de lector coacciona una cadena (también puede hacer una URL, etc.) en un BufferedReader. line-seqentrega una secuencia perezosa. Exigir el siguiente elemento de la secuencia perezosa resulta en una línea que se lee del lector.
Tenga en cuenta que desde Clojure 1.7 en adelante, también puede usar transductores para leer archivos de texto.
Número 3: cómo escribir en un nuevo archivo.
(use 'clojure.java.io)(with-open [wrtr (writer "/tmp/test.txt")](.write wrtr "Line to be written"))
Nuevamente, with-opense encarga de que BufferedWriteresté cerrado al final del cuerpo. Writer coacciona una cadena en un BufferedWriter, que se usa mediante la interoperabilidad java:(.write wrtr "something").
También podría usar spit, lo contrario de slurp:
(spit "/tmp/test.txt""Line to be written")
Número 4: agregue una línea a un archivo existente.
(use 'clojure.java.io)(with-open [wrtr (writer "/tmp/test.txt":append true)](.write wrtr "Line to be appended"))
Igual que el anterior, pero ahora con la opción de agregar.
O de nuevo con spit, lo contrario de slurp:
(spit "/tmp/test.txt""Line to be written":append true)
PD: Para ser más explícito sobre el hecho de que estás leyendo y escribiendo en un archivo y no en otra cosa, primero puedes crear un objeto de archivo y luego convertirlo en un BufferedReadero escritor:
Muchas gracias por tu respuesta detallada. Me alegra conocer la forma recomendada de File IO (archivo de texto) en 1.3. Parece que ha habido algunas bibliotecas sobre File IO (clojure.contrb.io, clojure.contrib.duck-streams y algunos ejemplos directamente usando Java BufferedReader FileInputStream InputStreamReader) que me hicieron más confuso. Además, hay poca información sobre Clojure 1.3, especialmente en japonés (mi idioma natural) Gracias.
jolly-san
Hola jolly-san, tnx por aceptar mi respuesta! Para su información, clojure.contrib.duck-streams ahora está en desuso. Esto posiblemente se suma a la confusión.
Michiel Borkent
Es decir, (with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))rinde en IOException Stream closedlugar de una colección de líneas. ¿Qué hacer? Sin embargo, estoy obteniendo buenos resultados con la respuesta de @satyagraha.
0dB
44
Esto tiene que ver con la pereza. Cuando utiliza el resultado de line-seq fuera de with-open, que ocurre cuando imprime su resultado en REPL, el lector ya está cerrado. Una solución es envolver el line-seq dentro de un doall, lo que obliga a la evaluación de inmediato. (with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
Michiel Borkent
Para los principiantes reales como yo, tenga en cuenta que los doseqretornos nilpueden conducir a tiempos tristes sin valor de retorno.
Oct
33
Si el archivo cabe en la memoria, puede leerlo y escribirlo con sorbo y escupir:
(def s (slurp "filename.txt"))
(s ahora contiene el contenido de un archivo como una cadena)
(spit "newfile.txt" s)
Esto crea newfile.txt si no sale y escribe el contenido del archivo. Si desea agregar al archivo, puede hacerlo
(spit "filename.txt" s :append true)
Para leer o escribir un archivo en línea, usaría el lector y escritor de Java. Están envueltos en el espacio de nombres clojure.java.io:
Tenga en cuenta que la diferencia entre slurp / spit y los ejemplos de lector / escritor es que el archivo permanece abierto (en las declaraciones let) en este último y la lectura y la escritura se almacenan en el búfer, por lo tanto, son más eficientes al leer / escribir repetidamente en un archivo.
Gracias Paul Podría obtener más información mediante sus códigos y sus comentarios, que son claros en el punto que se centra en responder mi pregunta. Muchas gracias.
jolly-san
Gracias por agregar información sobre métodos de nivel ligeramente inferior que no figuran en la respuesta (excelente) de Michiel Borkent sobre los mejores métodos para casos típicos.
Marte
@Mars Gracias. En realidad, respondí esta pregunta primero, pero la respuesta de Michiel tiene más estructura y eso parece ser muy popular.
Paul
Él hace un buen trabajo con los casos habituales, pero usted proporcionó otra información. Por eso es bueno que SE permita múltiples respuestas.
Marte
6
Con respecto a la pregunta 2, a veces se desea que la secuencia de líneas se devuelva como un objeto de primera clase. Para obtener esto como una secuencia perezosa, y todavía tener el archivo cerrado automáticamente en EOF, utilicé esta función:
No creo que la anidación defnsea un Clojure ideomático. Su read-next-line, por lo que yo entiendo, es visible fuera de su read-linesfunción. Es posible que hayas usado un (let [read-next-line (fn [] ...))en su lugar.
kristianlm
Creo que su respuesta sería un poco mejor si devolviera la función creada (cerrándose sobre el lector abierto) en lugar de ser globalmente vinculante.
Ward,
1
Así es como se lee todo el archivo.
Si el archivo está en el directorio de recursos, puede hacer esto:
Esto supone que su archivo de datos se mantiene en el directorio de recursos y que la primera línea es información de encabezado que se puede descartar.
Respuestas:
Suponiendo que solo estamos haciendo archivos de texto aquí y no algunas cosas binarias locas.
Número 1: cómo leer un archivo completo en la memoria.
No se recomienda cuando es un archivo realmente grande.
Número 2: cómo leer un archivo línea por línea.
La
with-open
macro se encarga de que el lector esté cerrado al final del cuerpo. La función de lector coacciona una cadena (también puede hacer una URL, etc.) en unBufferedReader
.line-seq
entrega una secuencia perezosa. Exigir el siguiente elemento de la secuencia perezosa resulta en una línea que se lee del lector.Tenga en cuenta que desde Clojure 1.7 en adelante, también puede usar transductores para leer archivos de texto.
Número 3: cómo escribir en un nuevo archivo.
Nuevamente,
with-open
se encarga de queBufferedWriter
esté cerrado al final del cuerpo. Writer coacciona una cadena en unBufferedWriter
, que se usa mediante la interoperabilidad java:(.write wrtr "something").
También podría usar
spit
, lo contrario deslurp
:Número 4: agregue una línea a un archivo existente.
Igual que el anterior, pero ahora con la opción de agregar.
O de nuevo con
spit
, lo contrario deslurp
:PD: Para ser más explícito sobre el hecho de que estás leyendo y escribiendo en un archivo y no en otra cosa, primero puedes crear un objeto de archivo y luego convertirlo en un
BufferedReader
o escritor:La función de archivo también está en clojure.java.io.
PS2: a veces es útil poder ver cuál es el directorio actual (así que "."). Puede obtener la ruta absoluta de dos maneras:
o
fuente
(with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))
rinde enIOException Stream closed
lugar de una colección de líneas. ¿Qué hacer? Sin embargo, estoy obteniendo buenos resultados con la respuesta de @satyagraha.(with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
doseq
retornosnil
pueden conducir a tiempos tristes sin valor de retorno.Si el archivo cabe en la memoria, puede leerlo y escribirlo con sorbo y escupir:
(s ahora contiene el contenido de un archivo como una cadena)
Esto crea newfile.txt si no sale y escribe el contenido del archivo. Si desea agregar al archivo, puede hacerlo
Para leer o escribir un archivo en línea, usaría el lector y escritor de Java. Están envueltos en el espacio de nombres clojure.java.io:
Tenga en cuenta que la diferencia entre slurp / spit y los ejemplos de lector / escritor es que el archivo permanece abierto (en las declaraciones let) en este último y la lectura y la escritura se almacenan en el búfer, por lo tanto, son más eficientes al leer / escribir repetidamente en un archivo.
Aquí hay más información: slurp spit clojure.java.io Java's BufferedReader Java's Writer
fuente
Con respecto a la pregunta 2, a veces se desea que la secuencia de líneas se devuelva como un objeto de primera clase. Para obtener esto como una secuencia perezosa, y todavía tener el archivo cerrado automáticamente en EOF, utilicé esta función:
fuente
defn
sea un Clojure ideomático. Suread-next-line
, por lo que yo entiendo, es visible fuera de suread-lines
función. Es posible que hayas usado un(let [read-next-line (fn [] ...))
en su lugar.Así es como se lee todo el archivo.
Si el archivo está en el directorio de recursos, puede hacer esto:
recuerde requerir / usar
clojure.java.io
.fuente
fuente
Para leer un archivo línea por línea, ya no necesita recurrir a la interoperabilidad:
Esto supone que su archivo de datos se mantiene en el directorio de recursos y que la primera línea es información de encabezado que se puede descartar.
fuente