Comment écrire dans un fichier en Scala?

pour la lecture, il y a l'abstraction utile Source . Comment puis-je écrire des lignes dans un fichier texte?

145
demandé sur Peter Mortensen 2011-01-05 15:58:58

14 réponses

Edit (September 2011): since Eduardo Costa asks about Scala2.9, et depuis Rick-777 commentaires que scalax.IO commit history est pratiquement inexistante depuis mi-2009...

Scala-IO a changé de Lieu: voir son GitHub repo , de Jesse Eichar (aussi le SO ):

le projet-cadre Scala IO consiste en quelques sous-projets pour différents aspects et extensions d'IO.

Il y a deux composantes principales de Scala IO:

  • Core - Core traite principalement de la lecture et de l'écriture de données à destination et en provenance de sources et de puits arbitraires. Les traits de la pierre angulaire sont Input , Output et Seekable qui fournir les API de base.

    Les autres classes d'importance sont Resource , ReadChars et WriteChars .
  • File - File est une API File (appelée Path ) qui est basée sur une combinaison de Java 7 NIO filesystem et SBT PathFinder APIs.

    Path et FileSystem sont les principaux points d'entrée dans la Scala IO Fichier API.
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)

réponse Originale à cette question (janvier 2011), avec la vieille place de la scala-io:

si vous ne voulez pas attendre Scala2.9, Vous pouvez utiliser la bibliothèque scala-incubator / scala-io .

(comme mentionné dans " pourquoi Scala Source ne ferme-t-elle pas le flux entrant sous-jacent? ")

voir les échantillons

"
{ // 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)
  }
67
répondu VonC 2017-05-23 11:47:28

C'est l'une des fonctionnalités manquantes de la norme Scala que j'ai trouvé si utile que je l'ajoute à ma bibliothèque personnelle. (Vous devriez probablement avoir une bibliothèque personnelle, trop.) Le code est ainsi:

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

et il est utilisé comme ceci:

import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
  data.foreach(p.println)
}
208
répondu Rex Kerr 2014-10-10 23:51:05

semblable à la réponse de Rex Kerr, mais plus générique. Tout d'abord, j'utilise une fonction d'aide:

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

alors je l'utilise comme:

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

et

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

etc.

48
répondu Jus12 2011-03-07 10:05:46

une réponse 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()
  }
33
répondu samthebest 2016-11-03 16:42:51

donnant une autre réponse, parce que mes modifications d'autres réponses ont été rejetées.

C'est la réponse la plus concise et simple (similaire à Garret Hall's)

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

c'est similaire à Jus12, mais sans la verbosité et avec le correct code style

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

Note vous ne devez PAS les accolades pour try finally , ni lambdas, et notez l'utilisation de espace réservé à la syntaxe. Notez également mieux nommer.

20
répondu samthebest 2013-11-23 11:31:46

une doublure pour sauvegarder/lire à / de String , en utilisant 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
}

cela ne convient pas pour les gros fichiers, mais fera l'affaire.

quelques liens:

de java.nio.fichier.Fichier.Ecrivez

java.lang.Chaîne.getBytes

scala.collection.JavaConverters

scala.collection.immuable.Liste.mkString

13
répondu Nick Zalutskiy 2013-12-01 20:21:21

Voici une doublure concise en utilisant la bibliothèque de compilateurs Scala:

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

alternativement, si vous voulez utiliser les bibliothèques Java, vous pouvez faire ce hack:

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

à Partir de scala écrire chaîne de fichier en une seule instruction

13
répondu Garrett Hall 2017-05-23 12:18:22

une micro-bibliothèque j'ai écrit: https://github.com/pathikrit/better-files

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

ou

file << "Hello" << "\n" << "World"
5
répondu pathikrit 2015-09-14 19:44:16

après avoir passé en revue toutes ces réponses sur la façon d'écrire facilement un fichier dans Scala, et certains d'entre eux sont assez sympa, j'ai eu trois problèmes:

  1. Dans le Jus12 la réponse de , l'utilisation de nourrissage pour l'utilisation de la méthode d'assistance n'est pas évidente pour Scala/FP débutants
  2. doit encapsuler les erreurs de niveau inférieur avec scala.util.Try
  3. doit montrer aux développeurs Java nouveaux à Scala / FP comment correctement nid dépendant ressources de sorte que la méthode close est effectuée sur chaque ressource dépendante dans l'ordre inverse - Note: fermeture des ressources dépendantes dans l'ordre inverse surtout en cas de défaillance est une exigence rarement comprise de la java.lang.AutoCloseable spécification qui tend à conduire à très pernicieux et difficile de trouver des bogues et courir time failures

avant de commencer, mon but n'est pas la concision. C'est pour faciliter la compréhension pour les débutants en Scala/FP, typiquement ceux qui viennent de Java. À la fin, je vais tirer tous les morceaux ensemble, et puis augmenter la concision.

tout d'abord, la méthode using doit être mise à jour pour utiliser Try (encore une fois, la concision n'est pas le but ici). Il sera renommé 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()
    )

le début de la méthode tryUsingAutoCloseable ci-dessus pourrait être déroutant parce qu'il semble avoir deux listes de paramètres au lieu de la liste de paramètre simple habituelle. Ceci est appelé le nourrissage. Et je ne vais pas entrer dans les détails comment fonctionne currying ou où il est occasionnellement utile. Il s'avère que pour ce problème particulier de l'espace, c'est le bon outil pour le travail.

ensuite, nous avons besoin de créer la méthode, tryPrintToFile , qui va créer un (ou écraser un existant) File et écrire List[String] . Il utilise un FileWriter qui est encapsulé par un BufferedWriter qui est à son tour relayée par un PrintWriter . Et pour élever la performance, une taille de tampon par défaut beaucoup plus grande que la valeur par défaut pour BufferedWriter est définie, defaultBufferSize , et assignée la valeur 65536.

Voici le code (et encore, la concision n'est pas le but ici):

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

la méthode ci-dessus tryPrintToFile est utile dans qu'il faut un List[String] comme entrée et l'envoie à un File . Créons maintenant une méthode tryWriteToFile qui prend un String et l'écrit à un File .

voici le code (et je vous laisse deviner la priorité de concision ici):

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

enfin, il est utile de pouvoir récupérer le contenu d'un File comme un String . Tandis que scala.io.Source fournit une méthode commode pour obtenir facilement le contenu d'un File , la méthode close doit être utilisée sur le Source pour libérer les poignées JVM et système de fichiers sous-jacents. Si ce n'est pas le cas, la ressource n'est pas libérée avant que le JVM GC (collecteur D'ordures) n'arrive à libérer l'instance Source elle-même. Et même alors, il n'y a qu'une faible garantie JVM que la méthode finalize sera appelée par le GC à close la ressource. Cela signifie qu'il est de la responsabilité du client d'appeler explicitement la méthode close , tout comme il est de la responsabilité d'un client de tall close sur une instance de java.lang.AutoCloseable . Pour cela, nous avons besoin d'une deuxième définition de la méthode d'utilisation qui traite scala.io.Source .

voici le code pour ceci (toujours pas concise):

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

et voici un exemple d'utilisation de celui-ci dans un lecteur de fichier en continu de ligne super simple (Utilisant actuellement pour lire les fichiers délimités tab à partir de la sortie de la base de données):

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

An une version mise à jour de la fonction ci-dessus a été fournie en réponse à une question différente mais liée à StackOverflow .


maintenant, en mettant tout cela en relation avec les importations extraites (ce qui rend beaucoup plus facile à coller dans la feuille de travail Scala présent à la fois dans Scalaide Eclipse et IntelliJ Scala plugin pour le rendre facile à Dumper sortie sur le bureau pour être plus facilement examiné avec un éditeur de texte), voici à quoi ressemble le code (avec une concision accrue):

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

en tant que novice de Scala/FP, j'ai brûlé de nombreuses heures (dans la plupart des cas la frustration de gratter la tête) en gagnant les connaissances et les solutions ci-dessus. J'espère que cela aidera d'autres Scala/FP newbies obtenir sur cette bosse d'apprentissage particulier plus rapidement.

4
répondu chaotic3quilibrium 2017-05-27 02:02:01

voici un exemple d'écriture de quelques lignes à un fichier en utilisant 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
2
répondu Chris Martin 2015-08-28 09:12:48

pour surpasser samthebest et les contributeurs avant lui, j'ai amélioré le nom et la concision:

  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))
1
répondu Epicurist 2014-08-18 12:36:35

Pas de dépendances, avec la gestion d'erreur

  • utilise exclusivement des méthodes de la bibliothèque standard
  • crée des répertoires pour le fichier, si nécessaire
  • utilise Either pour le traitement des erreurs

Code

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

Utilisation

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
répondu Matthias Braun 2017-01-16 11:08:55

cette ligne aide à écrire un fichier à partir d'un tableau ou D'une chaîne de caractères.

 new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }
0
répondu Vickyster 2017-05-12 09:42:30

si vous avez toujours Akka Streams dans votre projet, il fournit une doublure:

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

Akka docs > Fichier de transmission en continu IO

0
répondu akauppi 2018-01-11 15:21:31