tutorial tipos potencia listas funciones ejemplos datos comentarios codigos scala file-io scala-2.8

tipos - ¿Cómo escribir en un archivo en Scala?



scala tutorial (14)

Para leer, está la Source abstracción útil. ¿Cómo puedo escribir líneas en un archivo de texto?


Sin dependencias, con manejo de errores

  • Utiliza métodos de la biblioteca estándar exclusivamente
  • Crea directorios para el archivo, si es necesario
  • Utiliza Either para el manejo de errores

Código

def write(destinationFile: Path, fileContent: String): Either[Exception, Path] = write(destinationFile, fileContent.getBytes(StandardCharsets.UTF_8)) def write(destinationFile: Path, fileContent: Array[Byte]): Either[Exception, Path] = try { Files.createDirectories(destinationFile.getParent) // Return the path to the destinationFile if the write is successful Right(Files.write(destinationFile, fileContent)) } catch { case exception: Exception => Left(exception) }

Uso

val filePath = Paths.get("./testDir/file.txt") write(filePath , "A test") match { case Right(pathToWrittenFile) => println(s"Successfully wrote to $pathToWrittenFile") case Left(exception) => println(s"Could not write to $filePath. Exception: $exception") }


Aquí hay un ejemplo de scalaz-stream escribir algunas líneas en un archivo usando scalaz-stream .

import scalaz._ import scalaz.stream._ def writeLinesToFile(lines: Seq[String], file: String): Task[Unit] = Process(lines: _*) // Process that enumerates the lines .flatMap(Process(_, "/n")) // Add a newline after each line .pipe(text.utf8Encode) // Encode as UTF-8 .to(io.fileChunkW(fileName)) // Buffered write to the file .runLog[Task, Unit] // Get this computation as a Task .map(_ => ()) // Discard the result writeLinesToFile(Seq("one", "two"), "file.txt").run


Aquí hay un resumen conciso utilizando la biblioteca del compilador de Scala:

scala.tools.nsc.io.File("filename").writeAll("hello world")

Alternativamente, si quiere usar las bibliotecas de Java puede hacer esto:

Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}

Desde una cadena de escritura de scala a un archivo en una declaración


Dando otra respuesta, porque mis ediciones de otras respuestas fueron rechazadas.

Esta es la respuesta más concisa y simple (similar a la de Garret Hall)

File("filename").writeAll("hello world")

Esto es similar a Jus12, pero sin la verborrea y con el estilo de código correcto

def using[A <: {def close(): Unit}, B](resource: A)(f: A => B): B = try f(resource) finally resource.close() def writeToFile(path: String, data: String): Unit = using(new FileWriter(path))(_.write(data)) def appendToFile(path: String, data: String): Unit = using(new PrintWriter(new FileWriter(path, true)))(_.println(data))

Tenga en cuenta que NO necesita las llaves para try finally , ni lambdas, y tenga en cuenta el uso de la sintaxis placeholder. También tenga en cuenta una mejor denominación.


Después de revisar todas estas respuestas sobre cómo escribir fácilmente un archivo en Scala, y algunas de ellas son bastante buenas, tuve tres problemas:

  1. En la respuesta del Jus12 , el uso de currying para el método de ayuda de uso no es obvio para los principiantes de Scala / FP
  2. Necesita encapsular errores de nivel inferior con scala.util.Try
  3. Necesita mostrar a los desarrolladores de Java nuevos en Scala / FP cómo jerarquizar adecuadamente los recursos dependientes para que el método de close se realice en cada recurso dependiente en orden inverso. Nota: cerrar recursos dependientes en orden inverso ESPECIALMENTE EN CASO DE UNA FALTA es un requisito raramente entendido de la especificación java.lang.AutoCloseable que tiende a provocar errores muy perniciosos y difíciles de encontrar y fallas de tiempo de ejecución

Antes de comenzar, mi objetivo no es la concisión. Es para facilitar la comprensión de los principiantes de Scala / FP, generalmente los que provienen de Java. Al final, juntaré todas las partes y luego aumentaré la concisión.

Primero, el método de using debe actualizarse para usar Try (de nuevo, la concisión no es el objetivo aquí). Se renombrará como tryUsingAutoCloseable :

def tryUsingAutoCloseable[A <: AutoCloseable, R] (instantiateAutoCloseable: () => A) //parameter list 1 (transfer: A => scala.util.Try[R]) //parameter list 2 : scala.util.Try[R] = Try(instantiateAutoCloseable()) .flatMap( autoCloseable => try transfer(autoCloseable) finally autoCloseable.close() )

El comienzo del método tryUsingAutoCloseable anterior puede ser confuso porque parece tener dos listas de parámetros en lugar de la lista de parámetros únicos habitual. Esto se llama currying. Y no voy a entrar en detalles sobre cómo funciona el currículum o dónde es ocasionalmente útil. Resulta que para este espacio de problema en particular, es la herramienta adecuada para el trabajo.

A continuación, necesitamos crear el método, tryPrintToFile , que creará un (o sobrescribirá un File existente) y escribirá una List[String] . Utiliza un FileWriter que está encapsulado por un BufferedWriter que a su vez está encapsulado por un PrintWriter . Y para elevar el rendimiento, se define un tamaño de buffer predeterminado mucho más grande que el predeterminado para BufferedWriter , defaultBufferSize , y se le asigna el valor 65536.

Aquí está el código (y de nuevo, la concisión no es el objetivo aquí):

val defaultBufferSize: Int = 65536 def tryPrintToFile( lines: List[String], location: java.io.File, bufferSize: Int = defaultBufferSize ): scala.util.Try[Unit] = { tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method fileWriter => tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method bufferedWriter => tryUsingAutoCloseable(() => new java.io.PrintWriter(bufferedWriter)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method printWriter => scala.util.Try( lines.foreach(line => printWriter.println(line)) ) } } } }

El método tryPrintToFile anterior es útil porque toma una List[String] como entrada y la envía a un File . tryWriteToFile ahora un método tryWriteToFile que toma una String y la escribe en un File .

Aquí está el código (y le dejaré adivinar la prioridad de concisión aquí):

def tryWriteToFile( content: String, location: java.io.File, bufferSize: Int = defaultBufferSize ): scala.util.Try[Unit] = { tryUsingAutoCloseable(() => new java.io.FileWriter(location)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method fileWriter => tryUsingAutoCloseable(() => new java.io.BufferedWriter(fileWriter, bufferSize)) { //this open brace is the start of the second curried parameter to the tryUsingAutoCloseable method bufferedWriter => Try(bufferedWriter.write(content)) } } }

Finalmente, es útil poder obtener los contenidos de un File como una String . Si bien scala.io.Source proporciona un método conveniente para obtener fácilmente el contenido de un File , el método de close debe usarse en el Source para liberar los identificadores JVM subyacentes y el sistema de archivos. Si no se hace esto, el recurso no se libera hasta que JVM GC (Garbage Collector) se libera de la instancia de Source . Y aun así, solo hay una garantía de JVM débil. El GC llamará al método de finalize para close el recurso. Esto significa que es responsabilidad del cliente llamar explícitamente al método de close , al igual que es responsabilidad del cliente close de close una instancia de java.lang.AutoCloseable . Para esto, necesitamos una segunda definición del método de uso que maneje scala.io.Source .

Aquí está el código para esto (aún no siendo conciso):

def tryUsingSource[S <: scala.io.Source, R] (instantiateSource: () => S) (transfer: S => scala.util.Try[R]) : scala.util.Try[R] = Try(instantiateSource()) .flatMap( source => try transfer(source)) finally source.close() )

Y aquí hay un ejemplo de uso en un lector de archivos de transmisión de línea súper simple (que actualmente se utiliza para leer archivos delimitados por tabuladores desde la salida de la base de datos):

def tryProcessSource( file: java.io.File , parseLine: (String, Int) => List[String] = (line, index) => List(line) , filterLine: (List[String], Int) => Boolean = (values, index) => true , retainValues: (List[String], Int) => List[String] = (values, index) => values , isFirstLineNotHeader: Boolean = false ): scala.util.Try[List[List[String]]] = tryUsingSource(scala.io.Source.fromFile(file)) { source => scala.util.Try( ( for { (line, index) <- source.getLines().buffered.zipWithIndex values = parseLine(line, index) if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index) retainedValues = retainValues(values, index) } yield retainedValues ).toList //must explicitly use toList due to the source.close which will //occur immediately following execution of this anonymous function ) )

Se ha proporcionado una versión actualizada de la función anterior como respuesta a una pregunta de diferente pero relacionada .

Ahora, uniendo todo eso con las importaciones extraídas (lo que hace que sea mucho más fácil pegarlo en Scala Worksheet presente tanto en Eclipse ScalaIDE como en el complemento IntelliJ Scala para facilitar el volcado de la salida al escritorio para examinarlo más fácilmente con un editor de texto), así es como se ve el código (con mayor concisión):

import scala.io.Source import scala.util.Try import java.io.{BufferedWriter, FileWriter, File, PrintWriter} val defaultBufferSize: Int = 65536 def tryUsingAutoCloseable[A <: AutoCloseable, R] (instantiateAutoCloseable: () => A)(transfer: A => scala.util.Try[R]): scala.util.Try[R] = Try(instantiateAutoCloseable()) .flatMap( autoCloseable => try transfer(autoCloseable)) finally autoCloseable.close() ) def tryUsingSource[S <: scala.io.Source, R] (instantiateSource: () => S)(transfer: S => scala.util.Try[R]): scala.util.Try[R] = Try(instantiateSource()) .flatMap( source => try transfer(source)) finally source.close() ) def tryPrintToFile( lines: List[String], location: File, bufferSize: Int = defaultBufferSize ): Try[Unit] = tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter => tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter => tryUsingAutoCloseable(() => new PrintWriter(bufferedWriter)) { printWriter => Try(lines.foreach(line => printWriter.println(line))) } } } def tryWriteToFile( content: String, location: File, bufferSize: Int = defaultBufferSize ): Try[Unit] = tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter => tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter => Try(bufferedWriter.write(content)) } } def tryProcessSource( file: File, parseLine: (String, Int) => List[String] = (line, index) => List(line), filterLine: (List[String], Int) => Boolean = (values, index) => true, retainValues: (List[String], Int) => List[String] = (values, index) => values, isFirstLineNotHeader: Boolean = false ): Try[List[List[String]]] = tryUsingSource(Source.fromFile(file)) { source => Try( ( for { (line, index) <- source.getLines().buffered.zipWithIndex values = parseLine(line, index) if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index) retainedValues = retainValues(values, index) } yield retainedValues ).toList ) )

Como novato de Scala / FP, me he quemado muchas horas (en su mayoría frustración de rascarse la cabeza) ganando el conocimiento y las soluciones anteriores. Espero que esto ayude a otros principiantes Scala / FP a superar esta joroba de aprendizaje en particular más rápido.


Edición (septiembre de 2011): desde que Eduardo Costa pregunta por Scala2.9, y desde Rick-777 comenta que scalax.IO cometer historia es casi inexistente desde mediados de 2009 ...

Scala-IO ha cambiado de lugar: vea su repositorio GitHub , de Jesse Eichar (también en SO ):

El proyecto paraguas Scala IO consiste en algunos subproyectos para diferentes aspectos y extensiones de IO.
Hay dos componentes principales de Scala IO:

  • Core : Core principalmente se ocupa de leer y escribir datos hacia y desde fuentes y sumideros arbitrarios. Los rasgos de la piedra angular son Input , Output y Seekable que proporcionan la API principal.
    Otras clases de importancia son Resource , ReadChars y WriteChars .
  • Archivo : el archivo es una API de File (llamada Path ) que se basa en una combinación del sistema de archivos Java 7 NIO y las API de SBT PathFinder.
    Path y FileSystem son los principales puntos de entrada en la API Scala IO File.

import scalax.io._ val output:Output = Resource.fromFile("someFile") // Note: each write will open a new connection to file and // each write is executed at the begining of the file, // so in this case the last write will be the contents of the file. // See Seekable for append and patching files // Also See openOutput for performing several writes with a single connection output.writeIntsAsBytes(1,2,3) output.write("hello")(Codec.UTF8) output.writeStrings(List("hello","world")," ")(Codec.UTF8)

Respuesta original (enero de 2011), con el antiguo lugar para scala-io:

Si no quiere esperar a Scala2.9, puede usar la biblioteca scala-incubator / scala-io .
(como se menciona en " ¿Por qué Scala Source no cierra el InputStream subyacente? ")

Ver las muestras

{ // several examples of writing data import scalax.io.{ FileOps, Path, Codec, OpenOption} // the codec must be defined either as a parameter of ops methods or as an implicit implicit val codec = scalax.io.Codec.UTF8 val file: FileOps = Path ("file") // write bytes // By default the file write will replace // an existing file with the new data file.write (Array (1,2,3) map ( _.toByte)) // another option for write is openOptions which allows the caller // to specify in detail how the write should take place // the openOptions parameter takes a collections of OpenOptions objects // which are filesystem specific in general but the standard options // are defined in the OpenOption object // in addition to the definition common collections are also defined // WriteAppend for example is a List(Create, Append, Write) file.write (List (1,2,3) map (_.toByte)) // write a string to the file file.write("Hello my dear file") // with all options (these are the default options explicitely declared) file.write("Hello my dear file")(codec = Codec.UTF8) // Convert several strings to the file // same options apply as for write file.writeStrings( "It costs" :: "one" :: "dollar" :: Nil) // Now all options file.writeStrings("It costs" :: "one" :: "dollar" :: Nil, separator="||/n||")(codec = Codec.UTF8) }


Esta es una de las características que faltan en Scala estándar y que he encontrado tan útil que la agrego a mi biblioteca personal. (Probablemente también deberías tener una biblioteca personal). El código es más o menos así:

def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) { val p = new java.io.PrintWriter(f) try { op(p) } finally { p.close() } }

y se usa así:

import java.io._ val data = Array("Five","strings","in","a","file!") printToFile(new File("example.txt")) { p => data.foreach(p.println) }


Esta línea ayuda a escribir un archivo desde una matriz o cadena.

new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }


Para superar a Samthebest y los contribuyentes antes que él, he mejorado la nomenclatura y la concisión:

def using[A <: {def close() : Unit}, B](resource: A)(f: A => B): B = try f(resource) finally resource.close() def writeStringToFile(file: File, data: String, appending: Boolean = false) = using(new FileWriter(file, appending))(_.write(data))


Si de todos modos tiene Akka Streams en su proyecto, ofrece un diseño único:

def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = { Source.single(ByteString(s)).runWith(FileIO.toPath(p)) }

Akka hace> Streaming File IO


Similar a la respuesta de Rex Kerr, pero más genérica. Primero uso una función auxiliar:

/** * Used for reading/writing to database, files, etc. * Code From the book "Beginning Scala" * http://www.amazon.com/Beginning-Scala-David-Pollak/dp/1430219890 */ def using[A <: {def close(): Unit}, B](param: A)(f: A => B): B = try { f(param) } finally { param.close() }

Luego uso esto como:

def writeToFile(fileName:String, data:String) = using (new FileWriter(fileName)) { fileWriter => fileWriter.write(data) }

y

def appendToFile(fileName:String, textData:String) = using (new FileWriter(fileName, true)){ fileWriter => using (new PrintWriter(fileWriter)) { printWriter => printWriter.println(textData) } }

etc.


Un liners para guardar / leer a / de String , usando java.nio .

import java.nio.file.{Paths, Files, StandardOpenOption} import java.nio.charset.{StandardCharsets} import scala.collection.JavaConverters._ def write(filePath:String, contents:String) = { Files.write(Paths.get(filePath), contents.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE) } def read(filePath:String):String = { Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8).asScala.mkString }

Esto no es adecuado para archivos grandes, pero hará el trabajo.

Algunos enlaces:

java.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString



Una respuesta simple:

import java.io.File import java.io.PrintWriter def writeToFile(p: String, s: String): Unit = { val pw = new PrintWriter(new File(p)) try pw.write(s) finally pw.close() }