¿Cómo escribir en un archivo en Scala?


157

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


1
Si sabe cómo hacerlo en Java, puede usar lo mismo en Scala. ¿Es su pregunta específicamente con la biblioteca estándar de Scala?
wheaties

1
@wheaties sí, la mejor manera de hacer esto en scala
yura

Esta biblioteca es realmente buena: github.com/pathikrit/better-files
Robin

Respuestas:


71

Edite 2019 (8 años después), Scala-IO no es muy activo, si es que tiene alguno, Li Haoyi sugiere su propia biblioteca lihaoyi/os-lib, que presenta a continuación .

Junio ​​de 2019, Xavier Guihot menciona en su respuesta a la biblioteca Using, una utilidad para realizar la gestión automática de recursos.


Editar (septiembre de 2011): desde que Eduardo Costa pregunta por Scala2.9, y desde Rick-777 comenta que el historial de compromiso de scalax.IO es prácticamente inexistente desde mediados de 2009 ...

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

El proyecto general Scala IO consta de algunos subproyectos para diferentes aspectos y extensiones de IO.
Hay dos componentes principales de Scala IO:

  • Core : Core se ocupa principalmente de leer y escribir datos hacia y desde fuentes arbitrarias y sumideros. Los rasgos de piedra angular son Input, Outputy Seekableque proporcionan la API central.
    Otras clases de importancia son Resource, ReadCharsy WriteChars.
  • Archivo : el archivo es una API File(llamada Path) que se basa en una combinación del sistema de archivos Java 7 NIO y las API SBT PathFinder.
    Pathy FileSystemson los principales puntos de entrada a 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)
  }

15
¿Qué pasa con una versión Scala 2.9? :)
Eduardo Costa

El proyecto scalax parece muerto (sin compromisos desde junio de 2009). ¿Es esto correcto? scalax commit history
Rick-777

@Eduardo: He completado mi respuesta con el nuevo lugar para la biblioteca scala-io (que se ha actualizado para Scala2.9: github.com/jesseeichar/scala-io/issues/20 )
VonC

10
¿Es esta realmente la sugerencia actual para Scala 2.10? Utilice Scala IO? ¿Todavía no hay nada en el núcleo de Scala?
Phil

2
Nunca he usado scalax.io, pero a juzgar por estas líneas de ejemplo, parece que su diseño de API es bastante malo. La combinación de métodos para caracteres y datos binarios en una interfaz tiene poco sentido y muy probablemente conducirá a errores de codificación que son difíciles de encontrar. El diseño de java.io (Reader / Writer vs. InputStream / OutputStream) parece mucho mejor.
jcsahnwaldt Restablece a Monica el

211

Esta es una de las características que faltan en Scala estándar que he encontrado tan útil que la agrego a mi biblioteca personal. (Probablemente también debería tener una biblioteca personal). El código es 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)
}

1
new java.io.PrintWriter () usa la codificación predeterminada de la plataforma, lo que probablemente significa que el archivo de resultados no es muy portátil. Por ejemplo, si desea producir un archivo que luego puede enviar por correo electrónico, probablemente debería usar el constructor PrintWriter que le permite especificar una codificación.
jcsahnwaldt Restablece a Monica el

@JonaChristopherSahnwaldt: claro, en casos especiales es posible que desee especificar la codificación. El valor predeterminado para la plataforma es el valor predeterminado más sensible en promedio. Igual que con Source(codificación predeterminada por defecto). Por supuesto, puede agregar, por ejemplo, un enc: Option[String] = Noneparámetro después fsi considera que es una necesidad común.
Rex Kerr

66
@RexKerr - No estoy de acuerdo. Se debe especificar la codificación en casi todos los casos. La mayoría de los errores de codificación que encuentro ocurren porque las personas no entienden o no piensan en la codificación. Utilizan el valor predeterminado y ni siquiera lo saben porque demasiadas API les permiten salirse con la suya. Hoy en día, el valor predeterminado más sensato probablemente sería UTF-8. Quizás solo trabajas con inglés y otros idiomas que se pueden escribir en ASCII. Eres afortunado. Vivo en Alemania y tuve que arreglar más diéresis rotas de las que me gustaría recordar.
jcsahnwaldt Restablece a Monica el

3
@JonaChristopherSahnwaldt: esta es una razón para tener una codificación predeterminada sensata, no para obligar a todos a especificarla todo el tiempo. Pero si estás en una Mac y tus archivos escritos por Java son engullidos porque no están codificados en Mac OS Roman, no estoy seguro de que esté haciendo más bien que mal. Creo que es culpa de las plataformas que no hayan acordado un juego de caracteres. Como desarrollador individual, escribir una cadena realmente no va a resolver el problema. (Todos los desarrolladores que estén de acuerdo con UTF-8 lo harían, pero eso puede entrar por defecto).
Rex Kerr

@JonaChristopherSahnwaldt +10 por arreglar toda la diéresis rota. ¿No puedes usar un martillo, tal vez un perforador? ¿O ya son agujeros que necesitan relleno, tal vez este tipo pueda ayudar a youtube.com/watch?v=E-eBBzWEpwE Pero en serio, la influencia de ASCII es tan dañina en el mundo, de acuerdo que debería especificarse, y por defecto como UTF- 8
Davos

50

Similar a la respuesta de Rex Kerr, pero más genérico. 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() }

Entonces 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.


39
No me malinterpreten, me gusta su código y es muy educativo, pero cuanto más veo tales construcciones para problemas simples, más me recuerda el viejo chiste "hello world": ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 voto de mi parte).
greenoldman

44
Si estás escribiendo frases sencillas, nada importa. Si está escribiendo programas importantes (grandes con una necesidad continua de mantenimiento y evolución), este tipo de pensamiento conduce al tipo más rápido y pernicioso de degradación de la calidad del software.
Randall Schulz

3
No todos van a tener "ojos scala" hasta cierto nivel de práctica. Es divertido ver que este ejemplo de código proviene de Scala "Principiante"
asyncwait

asyncwait "begin" scala ... el título más irónico de la historia, nota: tengo el libro ... y justo ahora estoy empezando a entenderlo ... Supongo que estaba un paso antes de "principiante" jajaja: D ........
user1050817

1
El problema es menos los trucos Scala aquí, pero la verbosidad y el mal estilo. He editado esto a mucho más legible. Después de mi refactorización son solo 4 líneas (bueno, 4 con longitudes de línea IDE, utilicé 6 aquí para caber en la pantalla). En mi humilde opinión, ahora es una muy buena respuesta.
samthebest

38

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()
  }

1
@samthebest, ¿podría agregar las bibliotecas de donde importproviene?
Daniel

1
A partir de java 7, use java.nio.file en su lugar: def writeToFile (file: String, stringToWrite: String): Unit = {val writer = Files.newBufferedWriter (Paths.get (archivo)) intente writer.write (stringToWrite) finalmente writer.close ()}
E Shindler

20

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 verbosidad 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 try finally, ni las lambdas, y tenga en cuenta el uso de la sintaxis de marcador de posición. También tenga en cuenta una mejor denominación.


2
Lo sentimos, pero su código es imaginable, no cumple con el implementedrequisito previo. No puede usar el código que no está implementado. Quiero decir que debes decir cómo encontrarlo, ya que no está disponible por defecto y no es conocido.
Val

15

Aquí hay una línea concisa usando la biblioteca del compilador Scala:

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

Alternativamente, si desea utilizar las bibliotecas de Java, puede hacer este truco:

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

¿Qué importa? es decir, ¿de dónde viene el archivo?
Ben Hutchison

La biblioteca del compilador Scala.
Garrett Hall

3
Ya no es viable (no en Scala 2.11)
Brent Faust

1
¿De qué estás hablando? scala.tools.nsc.io.File("/tmp/myFile.txt")trabaja en Scala 2.11.8.

1
Ahora está en scala.reflect.io.File
Keith Nordstrom

13

One liners para guardar / leer desde / hacia 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


¿Por qué no es esto adecuado para archivos grandes?
Chetan Bhasin

2
@ChetanBhasin Probablemente porque writecopiará contentsa una nueva matriz de bytes en lugar de transmitirla al archivo, por lo tanto, en su punto máximo, usará el doble de memoria que contentssolo.
Daniel Werner el

10

Desafortunadamente para la respuesta principal, Scala-IO está muerto. Si no le importa usar una dependencia de terceros, considere usar mi biblioteca OS-Lib . Esto hace que trabajar con archivos, rutas y el sistema de archivos sea muy fácil:

// Make sure working directory exists and is empty
val wd = os.pwd/"out"/"splash"
os.remove.all(wd)
os.makeDir.all(wd)

// Read/write files
os.write(wd/"file.txt", "hello")
os.read(wd/"file.txt") ==> "hello"

// Perform filesystem operations
os.copy(wd/"file.txt", wd/"copied.txt")
os.list(wd) ==> Seq(wd/"copied.txt", wd/"file.txt")

Tiene líneas simples para escribir en archivos , agregar archivos , sobrescribir archivos y muchas otras operaciones útiles / comunes


Gracias por esta actualización. Votado He hecho referencia a su respuesta en la mía anterior para mayor visibilidad.
VonC

7

Una micro biblioteca que escribí: https://github.com/pathikrit/better-files

file.appendLine("Hello", "World")

o

file << "Hello" << "\n" << "World"

Aquí también: esta pregunta es uno de los principales éxitos al buscar en Google cómo escribir un archivo con scala. Ahora que su proyecto ha crecido, ¿es posible que desee ampliar un poco su respuesta?
asac

6

A partir Scala 2.13, la biblioteca estándar proporciona una utilidad de gestión de recursos dedicados: Using.

Se puede usar en este caso con recursos como PrintWritero BufferedWriterque se extiende AutoCloseablepara escribir en un archivo y, sin importar qué, cierre el recurso después:

  • Por ejemplo, con java.ioapi:

    import scala.util.Using, java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
  • O con java.nioapi:

    import scala.util.Using, java.nio.file.{Files, Paths}, java.nio.charset.Charset
    
    // val lines = List("hello", "world")
    Using(Files.newBufferedWriter(Paths.get("file.txt"), Charset.forName("UTF-8"))) {
      writer => lines.foreach(line => writer.write(line + "\n"))
    }

6

ACTUALIZACIÓN en 2019 / Sep / 01:

  • Comenzando con Scala 2.13, prefiera usar scala.util.
  • Se corrigió el error por el que finallyse tragaría el original Exceptionarrojado trysi el finallycódigo arrojaba unException

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

  1. En la respuesta de Jus12 , el uso de curry para usar el método auxiliar no es obvio para los principiantes de Scala / FP
  2. Necesita encapsular errores de nivel inferior con scala.util.Try
  3. Necesita mostrar los desarrolladores de Java nuevas a Scala / FP cómo adecuadamente nido dependientes de los recursos por lo que el closemétodo se lleva a cabo en cada recurso dependiente en orden inverso - Nota: el cierre de los recursos dependientes en el orden inverso en particular en caso de un fallo es un requisito rara vez se entiende de la java.lang.AutoCloseableespecificación que tiende a generar errores muy perniciosos y difíciles de encontrar y fallas en el tiempo de ejecución

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

Primero, el usingmétodo debe actualizarse para su uso Try(nuevamente, la concisión no es el objetivo aquí). Será renombrado a 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 => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(autoCloseable)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            autoCloseable.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

El comienzo del tryUsingAutoCloseablemétodo 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 curry. Y no entraré en detalles sobre cómo funciona el curry o dónde es útil ocasionalmente . Resulta que para este espacio problemático en particular, es la herramienta adecuada para el trabajo.

Luego, necesitamos crear un método, tryPrintToFileque creará un (o sobrescribirá uno existente) Filey escribirá un List[String]. Utiliza un FileWriterque está encapsulado por un BufferedWriterque a su vez está encapsulado por un PrintWriter. Y para elevar el rendimiento, BufferedWriterse define un tamaño de búfer predeterminado mucho mayor que el predeterminado para defaultBufferSize, y se le asigna el valor 65536.

Aquí está el código (y nuevamente, 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 tryPrintToFilemétodo anterior es útil porque toma un List[String]como entrada y lo envía a File. Ahora creemos un tryWriteToFilemétodo que tome a Stringy lo escriba en a File.

Aquí está el código (y te dejaré adivinar la prioridad de la 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 el contenido de a Filecomo a String. Si bien scala.io.Sourceproporciona un método conveniente para obtener fácilmente el contenido de a File, el closemétodo se debe utilizar Sourcepara liberar la JVM subyacente y los identificadores del sistema de archivos. Si esto no se hace, entonces el recurso no se libera hasta que el JVM GC (Garbage Collector) pueda liberar la Sourceinstancia en sí. Y aun así, solo hay una garantía débil de JVM de que el finalizemétodo será llamado por el GC al closerecurso. Esto significa que es responsabilidad del cliente llamar explícitamente al closemétodo, de la misma manera que es responsabilidad de un cliente hacer un alto closeen una instancia dejava.lang.AutoCloseable. Para esto, necesitamos una segunda definición del método de uso que maneja scala.io.Source.

Aquí está el código para esto (todavía no es conciso):

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)
  (transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(source)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            source.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

Y aquí hay un ejemplo de uso en un lector de archivos de transmisión de línea súper simple (actualmente utilizado para leer archivos delimitados por tabulaciones de 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 diferente pero relacionada de StackOverflow .


Ahora, uniendo todo eso con las importaciones extraídas (lo que hace que sea mucho más fácil pegar en Scala Worksheet presente tanto en Eclipse ScalaIDE como en el complemento IntelliJ Scala para que sea más fácil volcar la salida al escritorio para examinarla 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) //parameter list 1
  (transfer: A => scala.util.Try[R])  //parameter list 2
: scala.util.Try[R] =
  Try(instantiateAutoCloseable())
    .flatMap(
      autoCloseable => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(autoCloseable)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            autoCloseable.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)
  (transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source => {
        var optionExceptionTry: Option[Exception] = None
        try
          transfer(source)
        catch {
          case exceptionTry: Exception =>
            optionExceptionTry = Some(exceptionTry)
            throw exceptionTry
        }
        finally
          try
            source.close()
          catch {
            case exceptionFinally: Exception =>
              optionExceptionTry match {
                case Some(exceptionTry) =>
                  exceptionTry.addSuppressed(exceptionFinally)
                case None =>
                  throw exceptionFinally
              }
          }
      }
    )

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 un novato Scala / FP, he quemado muchas horas (en su mayoría frustración en la cabeza) ganando el conocimiento y las soluciones anteriores. Espero que esto ayude a otros novatos de Scala / FP a superar esta joroba de aprendizaje en particular más rápido.


2
Increíble actualización. El único problema es que ahora tiene como 100 líneas de código que podrían reemplazarse try-catch-finally. Aún amo tu pasión.
Observador

1
@Observer Yo afirmaría que es una declaración inexacta. El patrón que describo en realidad está reduciendo la cantidad de repeticiones que debe escribir un cliente para garantizar el manejo adecuado del cierre de AutoCloseables al tiempo que habilita el patrón de FP idiomático Scala de usar scala.util. Try. Si intentas lograr los mismos efectos que yo escribiendo manualmente los bloques try / catch / finally, creo que encontrarás que terminas con un poco más de lo que imaginas. Por lo tanto, hay un valor de legibilidad significativo al empujar todo el repetitivo a las 100 líneas de la función Scala.
chaotic3quilibrium

1
Lo siento si eso sonó ofensivo de alguna manera. Aún así, mi punto es que no hay necesidad de tal cantidad de código, porque lo mismo podría lograrse mediante un enfoque no funcional con mucha más simplicidad. Personalmente, escribiría try-finally con algunas verificaciones adicionales. Es solo más corto. Si quisiera usar envoltorios, ApacheUtils están ahí para usar todo el trabajo sucio. Además, todos los lectores / escritores estándar cierran las secuencias subyacentes, por lo que no se necesita su envoltura múltiple. PD: He cambiado mi voto de menos uno a más uno para apoyar sus esfuerzos. Entonces, por favor, no sospeches de mí con malas intenciones.
Observador

No se ofende.
chaotic3quilibrium

1
Entiendo tu punto de vista. Gracias por la discusión, tengo que pensarlo un poco. ¡Que tengas un buen día!
Observador

3

Aquí hay un ejemplo de cómo 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

1

Para superar a samthebest y a los contribuyentes antes que él, he mejorado el nombre 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))

Esto utiliza "tipear pato" que depende de la reflexión. Para muchos contextos, depender de la reflexión no es un iniciador.
chaotic3quilibrium

1

Sin dependencias, con manejo de errores.

  • Utiliza métodos de la biblioteca estándar exclusivamente
  • Crea directorios para el archivo, si es necesario.
  • Usos Eitherpara 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")
}

1

Actualización 2019:

Resumen: Java NIO (o NIO.2 para asíncrono) sigue siendo la solución de procesamiento de archivos más completa compatible con Scala. El siguiente código crea y escribe texto en un nuevo archivo:

import java.io.{BufferedOutputStream, OutputStream}
import java.nio.file.{Files, Paths}

val testFile1 = Paths.get("yourNewFile.txt")
val s1 = "text to insert in file".getBytes()

val out1: OutputStream = new BufferedOutputStream(
  Files.newOutputStream(testFile1))

try {
  out1.write(s1, 0, s1.length)
} catch {
  case _ => println("Exception thrown during file writing")
} finally {
  out1.close()
}
  1. Importar bibliotecas Java: IO y NIO
  2. Crea un Pathobjeto con tu nombre de archivo elegido
  3. Convierta el texto que desea insertar en un archivo en una matriz de bytes
  4. Obtenga su archivo como una secuencia: OutputStream
  5. Pase su matriz de bytes a la writefunción de su flujo de salida
  6. Cierra la corriente

1

Similar a esta respuesta , aquí hay un ejemplo con fs2(versión 1.0.4):

import cats.effect._

import fs2._
import fs2.io

import java.nio.file._

import scala.concurrent.ExecutionContext
import scala.language.higherKinds
import cats.syntax.functor._

object ScalaApp extends IOApp {

  def write[T[_]](p: Path, s: String)
                 (implicit F: ConcurrentEffect[T], cs: ContextShift[T]): T[Unit] = {
    Stream(s)
      .covary[T]
      .through(text.utf8Encode)
      .through(
        io.file.writeAll(
          p,
          scala.concurrent.ExecutionContext.global,
          Seq(StandardOpenOption.CREATE)
        )
      )
      .compile
      .drain
  }


  def run(args: List[String]): IO[ExitCode] = {

    implicit val executionContext: ExecutionContext =
      scala.concurrent.ExecutionContext.Implicits.global

    implicit val contextShift: ContextShift[IO] =
      IO.contextShift(executionContext)

    val outputFile: Path = Paths.get("output.txt")

    write[IO](outputFile, "Hello world\n").as(ExitCode.Success)

  }
}

0

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

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

0

Si de todos modos tiene Akka Streams en su proyecto, proporciona una línea:

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

Documentos de Akka> Streaming File IO

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.