Pour la lecture, il y a l'abstraction utile Source
. Comment puis-je écrire des lignes dans un fichier texte?
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
etSeekable
qui fournissent l’API de base.
Les autres classes d'importance sontResource
,ReadChars
etWriteChars
.- File - Le fichier est une API
File
(appeléePath
) basée sur une combinaison d'API système de fichiers Java 7 NIO et d'API SBT PathFinder.Path
etFileSystem
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)
}
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)
}
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.
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()
}
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.
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
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}
Une micro bibliothèque j'ai écrit: https://github.com/pathikrit/better-files
file.appendLine("Hello", "World")
ou
file << "Hello" << "\n" << "World"
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:
scala.util.Try
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.
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
Either
pour le traitement des erreursdef 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)
}
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")
}
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()
}
Path
avec le nom de fichier choisiOutputStream
write
de votre flux de sortieMalheureusement 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
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)
}
}
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))
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
À 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(())
Cette ligne permet d’écrire un fichier à partir d’un tableau ou d’une chaîne.
new PrintWriter(outputPath) { write(ArrayName.mkString("")); close }