Pour la lecture, il y a l'abstraction utile Source
. Comment puis-je écrire des lignes dans un fichier texte?
Pour la lecture, il y a l'abstraction utile Source
. Comment puis-je écrire des lignes dans un fichier texte?
Réponses:
Edit 2019 (8 ans plus tard), Scala-IO n'étant pas très actif, le cas échéant, Li Haoyi propose sa propre bibliothèque lihaoyi/os-lib
, qu'il présente ci-dessous .
Juin 2019, Xavier Guihot évoque dans sa réponse la bibliothèque Using
, un utilitaire de gestion automatique des ressources.
Edit (septembre 2011): depuis qu'Eduardo Costa pose des questions sur Scala2.9, et depuis que Rick-777 commente que l' historique des commit scalax.IO est quasiment inexistant depuis mi-2009 ...
Scala-IO a changé de place: voir son repo GitHub , de Jesse Eichar (également sur SO ):
Le projet cadre Scala IO se compose de quelques sous-projets pour différents aspects et extensions de l'IO.
Il existe deux composants principaux de Scala IO:
- Core - Core traite principalement de la lecture et de l'écriture de données depuis et vers des sources et des récepteurs arbitraires. Les traits de pierre angulaire sont
Input
,Output
etSeekable
qui fournissent l'API de base.
D'autres classes d'importance sontResource
,ReadChars
etWriteChars
.- File - File est une API
File
(appeléePath
) basée sur une combinaison du système de fichiers Java 7 NIO et des API SBT PathFinder.
Path
etFileSystem
sont les principaux points d'entrée dans l'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)
Réponse originale (janvier 2011), avec l'ancien endroit pour 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-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 de Scala standard que j'ai trouvée si utile que je l'ajoute à ma bibliothèque personnelle. (Vous devriez probablement aussi avoir une bibliothèque personnelle.) Le code va comme ceci:
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)
}
Source
(encodage par défaut par défaut). Vous pouvez bien sûr ajouter par exemple un enc: Option[String] = None
paramètre après f
si vous trouvez cela un besoin commun.
Similaire à 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, j'utilise ceci 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()
}
import
?
Donner une autre réponse, car mes modifications des autres réponses ont été rejetées.
C'est la réponse la plus concise et la plus simple (similaire à celle de Garret Hall)
File("filename").writeAll("hello world")
Ceci est similaire à Jus12, mais sans la verbosité et avec un style de code correct
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 d'accolades pour try finally
, ni de lambdas, et notez l'utilisation de la syntaxe d'espace réservé. Notez également une meilleure dénomination.
implemented
prérequis. Vous ne pouvez pas utiliser le code qui n'est pas implémenté. Je veux dire que vous devez dire comment le trouver car il n'est pas disponible par défaut et n'est pas bien connu.
Voici un concis one-liner utilisant la bibliothèque du compilateur Scala:
scala.tools.nsc.io.File("filename").writeAll("hello world")
Alternativement, si vous souhaitez utiliser les bibliothèques Java, vous pouvez faire ce hack:
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
scala.tools.nsc.io.File("/tmp/myFile.txt")
fonctionne dans Scala 2.11.8.
Un liners pour enregistrer / lire vers / depuis 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 fichiers volumineux, mais fera l'affaire.
Quelques liens:
java.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString
write
qu'il copiera contents
dans un nouveau tableau d'octets au lieu de le diffuser dans le fichier, ce qui à son apogée utilise deux fois plus de mémoire que contents
seul.
Malheureusement pour la meilleure réponse, 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 rend le travail avec les fichiers, les chemins et le système de fichiers très facile:
// 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 des lignes uniques pour écrire dans des fichiers , ajouter des fichiers , écraser des fichiers et de nombreuses autres opérations utiles / courantes
Une micro bibliothèque que j'ai écrite: https://github.com/pathikrit/better-files
file.appendLine("Hello", "World")
ou
file << "Hello" << "\n" << "World"
Au départ Scala 2.13
, la bibliothèque standard fournit un utilitaire de gestion des ressources dédié:Using
.
Il peut être utilisé dans ce cas avec des ressources telles que PrintWriter
ou BufferedWriter
qui s'étend AutoCloseable
pour écrire dans un fichier et, quoi qu'il en soit, fermer la ressource par la suite:
Par exemple, avec java.io
api:
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)
}
Ou avec java.nio
api:
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"))
}
MISE À JOUR le 2019 / Sep / 01:
finally
avalerait l'original Exception
jeté par try
si le finally
code jetait unException
Après avoir examiné toutes ces réponses sur la façon d'écrire facilement un fichier dans Scala, et certaines d'entre elles sont assez sympas, j'ai eu trois problèmes:
scala.util.Try
close
méthode soit exécutée sur chaque ressource dépendante dans l'ordre inverse - Remarque: la fermeture des ressources dépendantes dans l'ordre inverse SURTOUT EN CAS D'ÉCHEC est une exigence rarement comprise de la java.lang.AutoCloseable
spécification qui a tendance à conduire à des bogues très pernicieux et difficiles à trouver et à des échecs d'exécutionAvant de commencer, mon objectif n'est pas la concision. C'est pour faciliter la compréhension pour les débutants Scala / FP, généralement ceux venant de Java. À la toute fin, je vais rassembler tous les éléments, puis augmenter la concision.
Tout d'abord, la using
méthode doit être mise à jour pour être utilisée Try
(encore une fois, la concision n'est pas le but 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 => {
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
}
}
}
)
Le début de la tryUsingAutoCloseable
méthode ci-dessus peut prêter à confusion car elle semble avoir deux listes de paramètres au lieu de la liste de paramètres unique habituelle. C'est ce qu'on appelle le curry. Et je n'entrerai pas dans les détails du fonctionnement du curry ou de son emplacement occasionnel utile. Il s'avère que pour cet espace de problème particulier, c'est le bon outil pour le travail.
Ensuite, nous devons créer une méthode, tryPrintToFile
qui créera un (ou écrasera un existant) File
et écrira un fichier List[String]
. Il utilise un FileWriter
qui est encapsulé par un BufferedWriter
qui est à son tour encapsulé par un PrintWriter
. Et pour améliorer les performances, une taille de tampon par défaut beaucoup plus grande que la valeur par défaut pour BufferedWriter
est définie,defaultBufferSize
et affectée à la valeur 65536.
Voici le code (et encore une fois, 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 tryPrintToFile
méthode ci-dessus est utile en ce qu'elle prend un List[String]
comme entrée et l'envoie à un File
. Créons maintenant une tryWriteToFile
méthode qui prend a String
et l'écrit dans un File
.
Voici le code (et je vous laisse deviner ici la priorité de la concision):
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 fichier en File
tant que fichier String
. Bien qu'elle scala.io.Source
fournisse une méthode pratique pour obtenir facilement le contenu de a File
, la close
méthode doit être utilisée sur le Source
pour libérer les descripteurs de la JVM et du système de fichiers sous-jacents. Si cela n'est pas fait, la ressource n'est pas libérée tant que le JVM GC (Garbage Collector) ne parvient pas à libérer l' Source
instance elle-même. Et même dans ce cas, il n'y a qu'une faible garantie JVM que la finalize
méthode sera appelée par le GC à close
la ressource. Cela signifie qu'il est de la responsabilité du client d'appeler explicitement la close
méthode, tout comme il est de la responsabilité d'un client de calculer close
une instance dejava.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 => {
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
}
}
}
)
Et voici un exemple d'utilisation de celui-ci dans un lecteur de fichiers en ligne super simple (actuellement utilisé pour lire des fichiers délimités par des tabulations à 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
)
)
Une version mise à jour de la fonction ci-dessus a été fournie comme réponse à une question StackOverflow différente mais liée .
Maintenant, en réunissant tout cela avec les importations extraites (ce qui facilite beaucoup le collage dans la feuille de travail Scala présente dans les plugins Eclipse ScalaIDE et IntelliJ Scala pour faciliter le vidage de la sortie sur le bureau pour être plus facilement examinée 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) //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
)
}
En tant que débutant Scala / FP, j'ai brûlé de nombreuses heures (surtout dans une frustration éraflée) pour gagner les connaissances et les solutions ci-dessus. J'espère que cela aidera les autres débutants Scala / FP à surmonter plus rapidement cette bosse d'apprentissage particulière.
try-catch-finally
. Aimez toujours votre passion.
Voici un exemple d'écriture de quelques lignes dans un fichier à l'aide de 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
Pour surpasser samthebest et les contributeurs avant lui, j'ai amélioré la dénomination 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))
Either
pour la gestion 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 async) est toujours 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
objet avec le nom de fichier choisiOutputStream
write
fonction de votre flux de sortieSemblable à 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)
}
}
Si vous avez de toute façon Akka Streams dans votre projet, il fournit une ligne unique:
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}
Documentation Akka> E / S de fichiers en streaming