web-dev-qa-db-fra.com

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?

148
yura

Edit (septembre 2011): depuis Eduardo Costa pose des questions sur Scala2.9, et depuis Rick-777 commente que scalax.IO engage l'histoire est pratiquement inexistante depuis la mi-2009. ..

Scala-IO a changé de lieu: voir son GitHub repo , from Jesse Eichar (aussi sur SO ):

Le projet-cadre Scala IO consiste en quelques sous-projets portant sur différents aspects et extensions d’IO.
Scala IO comprend deux composants principaux:

  • Core - Core concerne principalement la lecture et l'écriture de données depuis et vers des sources et des puits arbitraires. Les caractéristiques de la pierre angulaire sont Input, Output et Seekable qui fournissent l’API de base.
    Les autres classes d'importance sont Resource, ReadChars et WriteChars.
  • File - Le fichier est une API File (appelée Path) basée sur une combinaison d'API système de fichiers Java 7 NIO et d'API SBT PathFinder.
    Path et FileSystem sont les principaux points d'entrée de l'API de fichier Scala IO.
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 (janvier 2011), avec l'ancienne place pour le scala-io:

Si vous ne voulez pas attendre Scala2.9, vous pouvez utiliser la bibliothèque scala-incubator/scala-io .
(comme indiqué dans " Pourquoi Scala Source ne ferme-t-il pas le InputStream 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)
  }
69
VonC

C’est l’une des fonctionnalités manquantes dans Scala standard que j’ai trouvé si utile que je l’ajoute à ma bibliothèque personnelle. (Vous devriez probablement aussi avoir une bibliothèque personnelle.) Le code va comme suit:

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 c'est utilisé comme ça:

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

Semblable à la réponse de Rex Kerr, mais plus générique. J'utilise d'abord une fonction d'assistance:

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

Ensuite, 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.

49
Jus12

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

Donner 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 la plus simple (semblable à celle de Garret Hall)

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

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

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

Notez que vous n'avez PAS besoin des accolades pour try finally, ni des lambdas, et notez l'utilisation de la syntaxe des espaces réservés. Notez également une meilleure désignation.

20
samthebest

Une ligne pour enregistrer/lire dans/à partir 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 aux gros fichiers, mais fera le travail.

Quelques liens:

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

13
Nick Zalutskiy

Voici une liste concise utilisant la bibliothèque de compilateur Scala:

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

Sinon, si vous souhaitez utiliser les bibliothèques Java, vous pouvez procéder comme suit:

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

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

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

ou

file << "Hello" << "\n" << "World"
6
pathikrit

Après avoir passé en revue toutes ces réponses sur la manière d’écrire facilement un fichier dans Scala, et certaines d’entre elles sont plutôt agréables, j’ai eu trois problèmes:

  1. Dans la réponse de Jus12's , l'utilisation du currying pour la méthode d'aide à l'aide n'est pas évidente pour les débutants en Scala/PF
  2. Besoin d'encapsuler les erreurs de niveau inférieur avec scala.util.Try
  3. Doit montrer aux développeurs Java débutant dans Scala/FP comment imbriquer correctement les ressources dépendantes de sorte que la méthode close soit exécutée sur chaque ressource dépendante dans l'ordre inverse - Remarque: la fermeture des ressources dépendantes dans l'ordre inverse ESPECIALLY IN L'ÉVÉNEMENT D'UN ÉCHEC est une exigence rarement comprise de la spécification Java.lang.AutoCloseable qui tend à générer des bogues et des échecs très pernicieux et difficiles à trouver.

Avant de commencer, mon objectif n'est pas la concision. Il s'agit de faciliter la compréhension des débutants en Scala/FP, généralement issus de Java. À la toute fin, je vais rassembler tous les éléments, 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 l'objectif ici). Il sera renommé en 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 peut être source de confusion, car il semble y avoir deux listes de paramètres au lieu de la liste habituelle de paramètres uniques. Cela s'appelle currying. Et je n’entrerai pas dans les détails sur le fonctionnement du curry ou sur son utilité occasionnellement. Il s'avère que pour cet espace de problèmes particulier, c'est le bon outil pour le travail.

Ensuite, nous devons créer une méthode, tryPrintToFile, qui créera (ou écrasera une existante) File et écrira un List[String]. Il utilise une FileWriter qui est encapsulée par une BufferedWriter qui est à son tour encapsulée par une PrintWriter. Et pour améliorer les performances, une taille de tampon par défaut beaucoup plus grande que celle par défaut pour BufferedWriter est définie, defaultBufferSize et la valeur 65536 est affectée.

Voici le code (et encore une fois, la concision n'est pas l'objectif 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 tryPrintToFile ci-dessus est utile car elle prend un List[String] en entrée et l'envoie à un File. Créons maintenant une méthode tryWriteToFile qui prend une String et l'écrit dans une File.

Voici le code (et je vous laisse deviner la priorité de la 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 extraire le contenu d'une File sous la forme String. Bien que scala.io.Source soit une méthode pratique pour obtenir facilement le contenu d'une File, la méthode close doit être utilisée sur la Source pour libérer la machine virtuelle Java et les descripteurs de système de fichiers. Si cela n'est pas fait, la ressource n'est pas publiée tant que JVM GC (Garbage Collector) ne parvient pas à libérer l'instance Source elle-même. Et même dans ce cas, il n’existe qu’une JVM faible qui garantisse que la méthode finalize sera appelée par le GC pour close la ressource. Cela signifie qu'il incombe au client d'appeler explicitement la méthode close, de la même manière qu'il incombe à un client de renvoyer tall close sur une instance de Java.lang.AutoCloseable. Pour cela, nous avons besoin d’une deuxième définition de la méthode using qui gère scala.io.Source.

Voici le code pour cela (toujours pas concis):

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 de transmission en ligne très simple (utilisant actuellement la lecture de fichiers délimités par des tabulations depuis 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
      )
  )

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


Maintenant, en regroupant tout cela avec les importations extraites (il est beaucoup plus facile de coller dans les feuilles de calcul Scala présentes dans les plug-ins Eclipse ScalaIDE et IntelliJ Scala afin de faciliter le dump de la sortie sur le bureau pour un examen plus facile 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 en Scala/FP, j'ai passé de nombreuses heures (frustration, principalement) à gagner des connaissances et à trouver des solutions. J'espère que cela aidera d'autres débutants Scala/FP à surmonter cette bosse d'apprentissage en particulier plus rapidement.

5
chaotic3quilibrium

Voici un exemple d’écriture de lignes dans un fichier avec 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
Chris Martin

Aucune dépendance, avec gestion des erreurs

  • Utilise des méthodes de la bibliothèque standard exclusivement
  • 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)
  }

Usage

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
Matthias Braun

Mise à jour 2019:

Résumé - Java NIO (ou NIO.2 pour asynchrone) reste la solution de traitement de fichiers la plus complète prise en charge par Scala. Le code suivant crée et écrit du texte dans un nouveau fichier:

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. Importer des bibliothèques Java: IO et NIO
  2. Créez un objet Path avec le nom de fichier choisi
  3. Convertissez le texte que vous souhaitez insérer dans un fichier dans un tableau d'octets
  4. Obtenez votre fichier sous forme de flux: OutputStream
  5. Passez votre tableau d'octets dans la fonction write de votre flux de sortie
  6. Fermer le flux
1
Janac Meena

Malheureusement pour la réponse principale, Scala-IO est mort. Si cela ne vous dérange pas d'utiliser une dépendance tierce, envisagez d'utiliser ma bibliothèque OS-Lib . Cela facilite le travail avec les fichiers, les chemins et le système de fichiers:

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

Il a une seule ligne pour écriture dans les fichiers , ajout de fichiers , écrasement de fichiers , et de nombreuses autres opérations utiles/courantes

1
Li Haoyi

Similaire à cette réponse , voici un exemple avec fs2 (version 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)

  }
}
1
Valy Dia

Pour dépasser 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
Epicurist

Si vous avez de toute façon Akka Streams dans votre projet, il fournit un one-liner:

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

Akka docs> Fichier de diffusion en continu IO

0
akauppi

À partir de Scala 2.13, la bibliothèque standard fournit un utilitaire dédié à la gestion des ressources: Using .

Il peut être utilisé dans ce cas avec des ressources telles que PrintWriter ou BufferedWriter qui s'étend AutoCloseable afin d'écrire dans un fichier et, quoi qu'il en soit, ferme la ressource après:

  • Par exemple, avec Java.io api:

    import scala.util.Using
    import Java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
    // scala.util.Try[Unit] = Success(())
    
  • Ou avec Java.nio api:

    import scala.util.Using
    import 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"))
    }
    // scala.util.Try[Unit] = Success(())
    
0
Xavier Guihot

Cette ligne permet d’écrire un fichier à partir d’un tableau ou d’une chaîne.

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