Come scrivere su un file in Scala?


157

Per la lettura, c'è l'astrazione utile Source. Come posso scrivere righe in un file di testo?


1
Se sai come farlo in Java, puoi usare lo stesso in Scala. La tua domanda è specifica con la biblioteca standard di Scala?
Wheaties

1
@wheaties Sì, il modo migliore per farlo in scala
yura,

Questa libreria è davvero ottima: github.com/pathikrit/better-files
Robin,

Libreria OS-Lib di
Lihaoyi

Risposte:


71

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, Outpute Seekableche forniscono l'API nucleo.
    Altre classi di importanza sono Resource, ReadCharse WriteChars.
  • File : il file è un'API File(chiamata Path) basata su una combinazione di filesystem NIO Java 7 e API SBT PathFinder.
    Pathe FileSystemsono 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)
  }

15
Che dire di una versione di Scala 2.9? :)
Eduardo Costa,

Il progetto scalax sembra morto (nessun impegno da giugno 2009). È giusto? cronologia del commit di scalax
Rick-777,

@Eduardo: ho completato la mia risposta con il nuovo posto per la biblioteca di scala-io (che è stata aggiornata per Scala2.9: github.com/jesseeichar/scala-io/issues/20 )
VonC,

10
È davvero questo il suggerimento attuale per Scala 2.10? Usa Scala IO? Non c'è ancora niente nel core della Scala?
Phil

2
Non ho mai usato scalax.io, ma a giudicare da queste righe di esempio, sembra che il suo design API sia piuttosto male. La combinazione di metodi per dati di caratteri e binari in un'unica interfaccia ha poco senso e molto probabilmente porterà a codificare bug difficili da trovare. Il design di java.io (Reader / Writer vs. InputStream / OutputStream) sembra molto migliore.
jcsahnwaldt Reinstate Monica,

211

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

1
new java.io.PrintWriter () utilizza la codifica predefinita della piattaforma, il che probabilmente significa che il file dei risultati non è molto portabile. Ad esempio, se si desidera produrre un file che è possibile inviare in seguito via e-mail, è consigliabile utilizzare il costruttore PrintWriter che consente di specificare una codifica.
jcsahnwaldt Reinstate Monica,

@JonaChristopherSahnwaldt - Certo, in casi speciali potresti voler specificare la codifica. Il valore predefinito per la piattaforma è il valore più ragionevole in media. Come con Source(codifica predefinita per impostazione predefinita). Puoi ovviamente aggiungere, ad esempio, un enc: Option[String] = Noneparametro dopo fse lo trovi un bisogno comune.
Rex Kerr,

6
@RexKerr - Non sono d'accordo. Si dovrebbe specificare la codifica in quasi tutti i casi. La maggior parte degli errori di codifica che incontro si verificano perché le persone non capiscono o non pensano alla codifica. Usano il valore predefinito e non lo sanno nemmeno perché troppe API gli consentono di cavarsela. Al giorno d'oggi, il default più sensato sarebbe probabilmente UTF-8. Forse lavori solo con inglese e altre lingue che possono essere scritte in ASCII. Sei fortunato. Vivo in Germania e ho dovuto riparare più umlaut rotte di quelle che mi interessa ricordare.
jcsahnwaldt Reinstate Monica,

3
@JonaChristopherSahnwaldt - Questa è una ragione per avere una codifica predefinita sensata, per non forzare tutti a specificarla continuamente. Ma se sei su un Mac e i tuoi file scritti da Java sono gobbledygook perché non sono codificati in Mac OS Roman, non sono sicuro che stia facendo più bene che male. Penso che sia colpa delle piattaforme che non hanno concordato un set di caratteri. Come singolo sviluppatore, digitare una stringa non risolverà davvero il problema. (Tutti gli sviluppatori che concordano su UTF-8 lo farebbero, ma poi quello può essere inserito come predefinito.)
Rex Kerr

@JonaChristopherSahnwaldt +10 per il fissaggio di tutte le umlaut rotte. Non puoi usare un martello, forse un perforatore? O sono già buchi che devono essere riempiti, forse questo ragazzo può aiutare youtube.com/watch?v=E-eBBzWEpwE Ma seriamente, l'influenza di ASCII è così dannosa nel mondo, concorda che dovrebbe essere specificato e predefinito come UTF- 8
Davos,

50

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.


39
Non fraintendetemi, mi piace il tuo codice ed è molto educativo, ma più vedo tali costrutti per problemi semplici, più mi ricorda la vecchia battuta "ciao mondo": ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 voto da parte mia).
Greenoldman,

4
Se stai scrivendo una riga, niente è importante. Se stai scrivendo programmi significativi (di grandi dimensioni con una continua necessità di manutenzione ed evoluzione), questo tipo di pensiero porta al tipo più rapido e dannoso di degrado della qualità del software.
Randall Schulz,

3
Non tutti avranno "occhi scala" fino a un certo livello di pratica - è divertente vedere questo esempio di codice proveniente da "Inizio" Scala
asyncwait,

asyncwait "inizio" scala ... il titolo più ironico di sempre, nota: ho il libro ... e proprio ora sto cominciando a capirlo..immagino di essere un passo prima di "principiante" lol: D ........
user1050817

1
Il problema è meno i trucchi alla Scala, ma la verbosità e lo stile scadente. L'ho modificato in modo molto più leggibile. Dopo il mio refactor sono solo 4 righe (beh, 4 con lunghezze IDE, usate 6 qui per adattarsi allo schermo). IMHO ora è una risposta molto bella.
Samthebest,

38

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

1
@samthebest potresti aggiungere le librerie importda cui provieni ?
Daniel,

1
A partire da java 7, utilizzare invece java.nio.file: def writeToFile (file: String, stringToWrite: String): Unit = {val writer = Files.newBufferedWriter (Paths.get (file)) provare writer.write (stringToWrite) finalmente writer.close ()}
E Shindler

20

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.


2
Siamo spiacenti, ma il tuo codice è immaginabile, non soddisfa il implementedprerequisito. Non è possibile utilizzare il codice non implementato. Voglio dire che devi dire come trovarlo poiché non è disponibile per impostazione predefinita e non è noto.
Val

15

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}

Quali importazioni? cioè da dove proviene File?
Ben Hutchison,

La libreria del compilatore Scala.
Garrett Hall,

3
Non più praticabile (non in Scala 2.11)
Brent Faust,

1
Di cosa stai parlando? scala.tools.nsc.io.File("/tmp/myFile.txt")opere in Scala 2.11.8.

1
Ora è in scala.reflect.io.File
Keith Nordstrom il

13

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


Perché questo non è adatto per file di grandi dimensioni?
Chetan Bhasin,

2
@ChetanBhasin Probabilmente perché writeverrà copiato contentssu un nuovo array di byte invece di trasmetterlo in streaming al file, quindi al suo apice utilizzando fino al doppio della memoria rispetto al contentssolo.
Daniel Werner,

10

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


Grazie per questo aggiornamento. Upvoted. Ho fatto riferimento alla tua risposta nella mia sopra per maggiore visibilità.
VonC,

7

Una micro biblioteca che ho scritto: https://github.com/pathikrit/better-files

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

o

file << "Hello" << "\n" << "World"

Anche qui - Questa domanda è uno dei maggiori successi quando si cerca su google come scrivere un file con scala - ora che il tuo progetto è diventato più grande, potresti voler espandere un po 'la tua risposta?
asac,

6

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 PrintWritero BufferedWriterche si estendono AutoCloseableper scrivere su un file e, in ogni caso, chiudere la risorsa in seguito:

  • Ad esempio, con java.ioapi:

    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.nioapi:

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

6

AGGIORNAMENTO il 2019 / Sep / 01:

  • A partire da Scala 2.13, preferisci usare scala.util.Using
  • Risolto il bug finallyche causava la deglutizione dell'originale Exceptiongenerato tryse il finallycodice 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:

  1. Nella risposta di Jus12 , l'uso del curry per l'utilizzo del metodo helper non è ovvio per i principianti di Scala / FP
  2. Deve incapsulare errori di livello inferiore con scala.util.Try
  3. Deve mostrare agli sviluppatori Java nuovi su Scala / FP come nidificare correttamente le risorse dipendenti in modo che il closemetodo 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.AutoCloseablespecifica che tende a portare a bug molto perniciosi e difficili da trovare e errori di runtime

Prima 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 usingmetodo 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 tryUsingAutoCloseablemetodo 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, tryPrintToFileche creerà un (o sovrascriverà un esistente) Filee scriverà un List[String]. Usa un FileWriterche è incapsulato da un BufferedWriterche è a sua volta incapsulato da un PrintWriter. E per migliorare le prestazioni, BufferedWriterviene 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 tryPrintToFilemetodo sopra è utile in quanto accetta un List[String]input e lo invia a File. Ora creiamo un tryWriteToFilemetodo che prende a Stringe 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 Filecome a String. Sebbene scala.io.Sourcefornisca un metodo conveniente per ottenere facilmente il contenuto di a File, il closemetodo deve essere utilizzato su Sourceper 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' Sourceistanza stessa. E anche allora, c'è solo una debole JVM che garantisce che il finalizemetodo verrà chiamato dal GC alla closerisorsa. 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 closeun'istanza dijava.lang.AutoCloseablescala.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.


2
Incredibile aggiornamento. L'unico problema è che ora hai circa 100 righe di codice che potrebbero essere sostituite try-catch-finally. Adoro ancora la tua passione.
Osservatore,

1
@Observer Vorrei affermare che si tratta di un'affermazione inesatta. Lo schema che sto descrivendo in realtà sta riducendo la quantità di piastra di caldaia che un cliente deve scrivere per garantire una corretta gestione della chiusura degli AutoCloseable, consentendo allo stesso tempo lo schema FP idiomatico Scala di usare scala.util. Try. Se provi a ottenere gli stessi effetti che ho scrivendo manualmente i blocchi try / catch / finally, penso che ti ritroverai con un po 'più di boilerplate di quanto immagini. Quindi, c'è un significativo valore di leggibilità nel spingere tutta la piastra della caldaia nelle 100 linee della funzione Scala.
chaotic3quilibrium,

1
Scusate se sembrava offensivo in alcun modo. Tuttavia, il mio punto è che non è necessario in tale quantità di codice, perché lo stesso potrebbe essere ottenuto attraverso un approccio non funzionale con molta più semplicità. Personalmente, scriverei try-finally con alcuni controlli aggiuntivi. È solo più breve. Se volessi usare i wrapper, ApacheUtils è lì per usare tutto il lavoro sporco. Inoltre, tutti i lettori / scrittori standard chiudono i flussi sottostanti, quindi non è necessario il multipwrap. PS: ho cambiato il mio voto da meno uno a più uno per supportare i tuoi sforzi. Quindi, per favore, non sospettarmi di cattive intenzioni.
Osservatore,

Nessuna offesa.
chaotic3quilibrium,

1
Capisco il tuo punto di vista. Grazie per la discussione, devo pensarci un po '. Buona giornata!
Osservatore,

3

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

1

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

Questo utilizza la "tipizzazione anatra" che dipende dalla riflessione. Per molti contesti, a seconda della riflessione è un antipasto.
chaotic3quilibrium

1

Nessuna dipendenza, con gestione degli errori

  • Utilizza esclusivamente i metodi della libreria standard
  • Crea directory per il file, se necessario
  • Utilizza Eitherper la gestione degli errori

Codice

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

uso

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

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()
}
  1. Importa librerie Java: IO e NIO
  2. Creare un Path oggetto con il nome del file scelto
  3. Converti il ​​testo che desideri inserire in un file in una matrice di byte
  4. Ottieni il tuo file come stream: OutputStream
  5. Passa l'array di byte nel flusso di output write funzione
  6. Chiudi il flusso

1

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

  }
}

0

Questa riga aiuta a scrivere un file da una matrice o una stringa.

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

0

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.