Per la lettura, c'è l'astrazione utile Source
. Come posso scrivere righe in un file di testo?
Per la lettura, c'è l'astrazione utile Source
. Come posso scrivere righe in un file di testo?
Risposte:
Modifica 2019 (8 anni dopo), essendo Scala-IO non molto attivo, se del caso, Li Haoyi suggerisce la sua biblioteca lihaoyi/os-lib
, che presenta di seguito .
Giugno 2019, Xavier Guihot menziona nella sua risposta la libreria Using
, un'utilità per eseguire la gestione automatica delle risorse.
Modifica (settembre 2011): da quando Eduardo Costa chiede di Scala2.9 e da quando Rick-777 commenta che la cronologia degli impegni di scalax.IO è praticamente inesistente dalla metà del 2009 ...
Scala-IO ha cambiato posto: vedi il suo repository GitHub , da Jesse Eichar (anche su SO ):
Il progetto ombrello Scala IO consiste in alcuni sottoprogetti per diversi aspetti ed estensioni di IO.
Esistono due componenti principali di Scala IO:
- Core : Core si occupa principalmente di leggere e scrivere dati da e verso fonti e pozzi arbitrari. I tratti pietra angolo sono
Input
,Output
eSeekable
che forniscono l'API nucleo.
Altre classi di importanza sonoResource
,ReadChars
eWriteChars
.- File : il file è un'API
File
(chiamataPath
) basata su una combinazione di filesystem NIO Java 7 e API SBT PathFinder.
Path
eFileSystem
sono i principali punti di ingresso nell'API File IO di Scala.
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)
Risposta originale (gennaio 2011), con il vecchio posto per scala-io:
Se non si desidera attendere Scala2.9, è possibile utilizzare la libreria scala-incubator / scala-io .
(come menzionato in " Perché Scala Source non chiude InputStream sottostante? ")
Vedi i campioni
{ // 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)
}
Questa è una delle funzionalità mancanti dallo standard Scala che ho trovato così utile da aggiungerlo alla mia libreria personale. (Probabilmente dovresti avere anche una libreria personale.) Il codice va così:
def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
val p = new java.io.PrintWriter(f)
try { op(p) } finally { p.close() }
}
ed è usato così:
import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
data.foreach(p.println)
}
Source
(codifica predefinita per impostazione predefinita). Puoi ovviamente aggiungere, ad esempio, un enc: Option[String] = None
parametro dopo f
se lo trovi un bisogno comune.
Simile alla risposta di Rex Kerr, ma più generico. Per prima cosa utilizzo una funzione di supporto:
/**
* 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() }
Quindi lo uso come:
def writeToFile(fileName:String, data:String) =
using (new FileWriter(fileName)) {
fileWriter => fileWriter.write(data)
}
e
def appendToFile(fileName:String, textData:String) =
using (new FileWriter(fileName, true)){
fileWriter => using (new PrintWriter(fileWriter)) {
printWriter => printWriter.println(textData)
}
}
eccetera.
Una semplice risposta:
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
da cui provieni ?
Dare un'altra risposta, perché le mie modifiche ad altre risposte sono state respinte.
Questa è la risposta più concisa e semplice (simile a quella di Garret Hall)
File("filename").writeAll("hello world")
Questo è simile a Jus12, ma senza la verbosità e con lo stile di codice corretto
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))
Nota che NON hai bisogno delle parentesi graffe per try finally
, né lambda, e nota l'uso della sintassi segnaposto. Nota anche una migliore denominazione.
implemented
prerequisito. Non è possibile utilizzare il codice non implementato. Voglio dire che devi dire come trovarlo poiché non è disponibile per impostazione predefinita e non è noto.
Ecco un one-liner conciso che utilizza la libreria di compilatori Scala:
scala.tools.nsc.io.File("filename").writeAll("hello world")
In alternativa, se si desidera utilizzare le librerie Java, è possibile eseguire questo hack:
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
Una fodera per il salvataggio / lettura da / verso String
, utilizzando 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
}
Questo non è adatto per file di grandi dimensioni, ma farà il lavoro.
Alcuni link:
java.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString
write
verrà copiato contents
su un nuovo array di byte invece di trasmetterlo in streaming al file, quindi al suo apice utilizzando fino al doppio della memoria rispetto al contents
solo.
Sfortunatamente per la risposta migliore, Scala-IO è morta. Se non ti dispiace usare una dipendenza di terze parti, considera l'utilizzo della mia libreria OS-Lib . Questo rende molto semplice lavorare con file, percorsi e filesystem:
// 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")
Ha una riga per la scrittura di file , l' aggiunta di file , la sovrascrittura di file e molte altre operazioni utili / comuni
Una micro biblioteca che ho scritto: https://github.com/pathikrit/better-files
file.appendLine("Hello", "World")
o
file << "Hello" << "\n" << "World"
A partire Scala 2.13
, la libreria standard fornisce un'utilità di gestione delle risorse dedicata:Using
.
Può essere utilizzato in questo caso con risorse come PrintWriter
o BufferedWriter
che si estendono AutoCloseable
per scrivere su un file e, in ogni caso, chiudere la risorsa in seguito:
Ad esempio, con 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)
}
O con 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"))
}
AGGIORNAMENTO il 2019 / Sep / 01:
finally
che causava la deglutizione dell'originale Exception
generato try
se il finally
codice avesse lanciato unException
Dopo aver esaminato tutte queste risposte su come scrivere facilmente un file in Scala, e alcune sono piuttosto carine, ho avuto tre problemi:
scala.util.Try
close
metodo venga eseguito su ciascuna risorsa dipendente in ordine inverso - Nota: la chiusura di risorse dipendenti in ordine inverso, IN PARTICOLARE NELL'EVENTO DI UN GUASTO è un requisito raramente compreso di la java.lang.AutoCloseable
specifica che tende a portare a bug molto perniciosi e difficili da trovare e errori di runtimePrima di iniziare, il mio obiettivo non è conciso. Serve a facilitare la comprensione per i principianti di Scala / FP, in genere quelli che provengono da Java. Alla fine, unirò tutti i bit e poi aumenterò la concisione.
Innanzitutto, il using
metodo deve essere aggiornato per essere utilizzato Try
(di nuovo, la concisione non è l'obiettivo qui). Sarà rinominato in 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
}
}
}
)
L'inizio del tryUsingAutoCloseable
metodo sopra potrebbe essere fonte di confusione perché sembra avere due elenchi di parametri anziché l'elenco di parametri singolo consueto. Questo si chiama curry. E non approfondirò come funziona il curry o dove si trova di tanto in tanto utile. Si scopre che per questo particolare spazio problematico, è lo strumento giusto per il lavoro.
Successivamente, dobbiamo creare un metodo, tryPrintToFile
che creerà un (o sovrascriverà un esistente) File
e scriverà un List[String]
. Usa un FileWriter
che è incapsulato da un BufferedWriter
che è a sua volta incapsulato da un PrintWriter
. E per migliorare le prestazioni, BufferedWriter
viene definita una dimensione del buffer predefinita molto più grande di quella predefinita ,defaultBufferSize
e viene assegnato il valore 65536.
Ecco il codice (e ancora, la concisione non è l'obiettivo qui):
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))
)
}
}
}
}
Il tryPrintToFile
metodo sopra è utile in quanto accetta un List[String]
input e lo invia a File
. Ora creiamo un tryWriteToFile
metodo che prende a String
e lo scrive in aFile
.
Ecco il codice (e ti farò indovinare la priorità della concisione qui):
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))
}
}
}
Infine, è utile essere in grado di recuperare il contenuto di a File
come a String
. Sebbene scala.io.Source
fornisca un metodo conveniente per ottenere facilmente il contenuto di a File
, il close
metodo deve essere utilizzato su Source
per rilasciare gli handle di file system e JVM sottostanti. Se ciò non viene fatto, la risorsa non viene rilasciata fino a quando JVM GC (Garbage Collector) non riesce a rilasciare l' Source
istanza stessa. E anche allora, c'è solo una debole JVM che garantisce che il finalize
metodo verrà chiamato dal GC alla close
risorsa. Ciò significa che è responsabilità del cliente chiamare esplicitamente il . Per questo, abbiamo bisogno di una seconda definizione del metodo using che gestisce .close
metodo, esattamente come è responsabilità di un cliente occuparsi di close
un'istanza dijava.lang.AutoCloseable
scala.io.Source
Ecco il codice per questo (non essere ancora 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
}
}
}
)
E qui ne è un esempio l'utilizzo in un lettore di file di streaming di linea super semplice (attualmente in uso per leggere file delimitati da tabulazioni dall'output del database):
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
)
)
Una versione aggiornata della funzione sopra è stata fornita come risposta a una domanda StackOverflow diversa ma correlata .
Ora, portando tutto questo insieme alle importazioni estratte (rendendo molto più facile incollare nel foglio di lavoro Scala presente sia nel plug-in Eclipse ScalaIDE sia nel plug-in IntelliJ Scala per semplificare il dump dell'output sul desktop per essere esaminato più facilmente con un editor di testo), ecco come appare il codice (con maggiore concisione):
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
)
}
Come novizio di Scala / FP, ho bruciato molte ore (soprattutto per la frustrazione della testa) guadagnando le conoscenze e le soluzioni di cui sopra. Spero che questo aiuti gli altri neofiti della Scala / FP a superare più velocemente questa particolare gobba di apprendimento.
try-catch-finally
. Adoro ancora la tua passione.
Ecco un esempio di come scrivere alcune righe in un file 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
Per superare Samthebest e i collaboratori prima di lui, ho migliorato la denominazione e la concisione:
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
per la gestione degli erroridef 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")
}
Aggiornamento 2019:
Riepilogo: Java NIO (o NIO.2 per asincrono) è ancora la soluzione di elaborazione dei file più completa supportata in Scala. Il codice seguente crea e scrive del testo in un nuovo file:
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
oggetto con il nome del file sceltoOutputStream
write
funzioneSimile a questa risposta , ecco un esempio con fs2
(versione 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)
}
}
Se hai comunque Akka Streams nel tuo progetto, ti fornisce un one-liner:
def writeToFile(p: Path, s: String)(implicit mat: Materializer): Unit = {
Source.single(ByteString(s)).runWith(FileIO.toPath(p))
}
Documenti Akka> Streaming File IO