Wie schreibe ich in eine Datei in Scala?


157

Zum Lesen gibt es die nützliche Abstraktion Source. Wie kann ich Zeilen in eine Textdatei schreiben?


1
Wenn Sie wissen, wie man das in Java macht, können Sie dasselbe in Scala verwenden. Ist Ihre Frage speziell mit der Scala-Standardbibliothek?
Wheaties

1
@ Wheaties ja beste Weg, dies in Scala
Yura

Diese Bibliothek ist wirklich gut: github.com/pathikrit/better-files
Robin

Lihaoyis OS-Lib-Bibliothek github.com/lihaoyi/os-lib
WeiChing

Antworten:


71

Bearbeiten Sie 2019 (8 Jahre später), da Scala-IO nicht sehr aktiv ist, schlägt Li Haoyi seine eigene Bibliothek vor lihaoyi/os-lib, die er unten vorstellt .

Im Juni 2019 erwähnt Xavier Guihot in seiner Antwort die Bibliothek Using, ein Dienstprogramm zur Durchführung der automatischen Ressourcenverwaltung.


Bearbeiten (September 2011): Da Eduardo Costa nach Scala2.9 fragt und Rick-777 kommentiert, dass die Commit-Historie von scalax.IO seit Mitte 2009 so gut wie nicht mehr existiert ...

Scala-IO hat den Platz gewechselt: siehe GitHub-Repo von Jesse Eichar (ebenfalls auf SO ):

Das Scala IO-Dachprojekt besteht aus einigen Teilprojekten für verschiedene Aspekte und Erweiterungen von IO.
Es gibt zwei Hauptkomponenten von Scala IO:

  • Core - Core befasst sich hauptsächlich mit dem Lesen und Schreiben von Daten zu und von beliebigen Quellen und Senken. Die Eckpfeiler sind Input, Outputund Seekabledie die Kern-API bereitstellen.
    Andere Klassen von Bedeutung sind Resource, ReadCharsund WriteChars.
  • Datei - Datei ist eine File(aufgerufene Path) API, die auf einer Kombination aus Java 7 NIO-Dateisystem und SBT PathFinder-APIs basiert.
    Pathund FileSystemsind die wichtigsten Einstiegspunkte in die Scala IO File API.
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)

Ursprüngliche Antwort (Januar 2011) mit dem alten Ort für Scala-Io:

Wenn Sie nicht auf Scala2.9 warten möchten, können Sie die Scala-Inkubator / Scala-Io- Bibliothek verwenden.
(wie unter " Warum schließt Scala Source den zugrunde liegenden InputStream nicht? ")

Siehe die Beispiele

{ // 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
Was ist mit einer Scala 2.9-Version? :)
Eduardo Costa

Das Scalax-Projekt scheint tot zu sein (keine Commits seit Juni 2009). Ist das richtig? Scalax Commit-Geschichte
Rick-777

@Eduardo: Ich habe meine Antwort mit dem neuen Platz für die Scala-Io-Bibliothek abgeschlossen (der für Scala2.9 aktualisiert wurde: github.com/jesseeichar/scala-io/issues/20 )
VonC

10
Ist das wirklich der aktuelle Vorschlag für Scala 2.10? Scala IO verwenden? Es gibt noch nichts in der Kern-Scala?
Phil

2
Ich habe scalax.io noch nie verwendet, aber nach diesen Beispielzeilen zu urteilen, scheint das API-Design ziemlich schlecht zu sein. Das Mischen von Methoden für Zeichen- und Binärdaten in einer Schnittstelle ist wenig sinnvoll und führt sehr wahrscheinlich zu schwer zu findenden Codierungsfehlern. Das Design von java.io (Reader / Writer vs. InputStream / OutputStream) scheint viel besser zu sein.
jcsahnwaldt Reinstate Monica

211

Dies ist eine der Funktionen, die in der Standard-Scala fehlen und die ich so nützlich fand, dass ich sie meiner persönlichen Bibliothek hinzufüge. (Sie sollten wahrscheinlich auch eine persönliche Bibliothek haben.) Der Code sieht folgendermaßen aus:

def printToFile(f: java.io.File)(op: java.io.PrintWriter => Unit) {
  val p = new java.io.PrintWriter(f)
  try { op(p) } finally { p.close() }
}

und es wird so verwendet:

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 () verwendet die Standardcodierung der Plattform, was wahrscheinlich bedeutet, dass die Ergebnisdatei nicht sehr portabel ist. Wenn Sie beispielsweise eine Datei erstellen möchten, die Sie später per E-Mail versenden können, sollten Sie wahrscheinlich den PrintWriter-Konstruktor verwenden, mit dem Sie eine Codierung angeben können.
jcsahnwaldt Reinstate Monica

@JonaChristopherSahnwaldt - Sicher, in besonderen Fällen möchten Sie möglicherweise die Codierung angeben. Die Standardeinstellung für die Plattform ist im Durchschnitt die sinnvollste Standardeinstellung. Gleich wie bei Source(Standardcodierung standardmäßig). Sie können natürlich zB einen enc: Option[String] = NoneParameter nach hinzufügen, fwenn Sie dies als häufigen Bedarf empfinden.
Rex Kerr

6
@ RexKerr - Ich bin anderer Meinung. In fast allen Fällen sollte die Kodierung angegeben werden. Die meisten Codierungsfehler treten auf, weil die Leute die Codierung nicht verstehen oder nicht darüber nachdenken. Sie verwenden die Standardeinstellung und wissen es nicht einmal, weil zu viele APIs sie damit davonkommen lassen. Heutzutage wäre UTF-8 wahrscheinlich der sinnvollste Standard. Vielleicht arbeiten Sie nur mit Englisch und anderen Sprachen, die in ASCII geschrieben werden können. Du Glückspilz. Ich lebe in Deutschland und musste mehr kaputte Umlaute reparieren, als ich mir merken möchte.
jcsahnwaldt Reinstate Monica

3
@JonaChristopherSahnwaldt - Dies ist ein Grund für eine sinnvolle Standardcodierung, nicht um jeden zu zwingen, sie ständig anzugeben. Aber wenn Sie auf einem Mac arbeiten und Ihre von Java geschriebenen Dateien gobbledygook sind, weil sie nicht mit Mac OS Roman codiert sind, bin ich mir nicht sicher, ob es mehr nützt als schadet. Ich denke, es ist die Schuld der Plattformen, dass sie sich nicht auf einen Zeichensatz geeinigt haben. Als einzelner Entwickler wird das Problem durch Eingabe eines Strings nicht wirklich gelöst. (Alle Entwickler, die sich auf UTF-8 einigen, würden dies tun, aber dann kann dies standardmäßig aktiviert werden.)
Rex Kerr

@JonaChristopherSahnwaldt +10 zum Reparieren des kaputten Umlauts. Kannst du keinen Hammer benutzen, vielleicht einen Locher? Oder sind es bereits Löcher, die gefüllt werden müssen? Vielleicht kann dieser Typ youtube.com/watch?v=E-eBBzWEpwE helfen. Aber im Ernst, der Einfluss von ASCII ist in der Welt so schädlich. 8
Davos

50

Ähnlich der Antwort von Rex Kerr, aber allgemeiner. Zuerst benutze ich eine Hilfsfunktion:

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

Dann benutze ich dies als:

def writeToFile(fileName:String, data:String) = 
  using (new FileWriter(fileName)) {
    fileWriter => fileWriter.write(data)
  }

und

def appendToFile(fileName:String, textData:String) =
  using (new FileWriter(fileName, true)){ 
    fileWriter => using (new PrintWriter(fileWriter)) {
      printWriter => printWriter.println(textData)
    }
  }

etc.


39
Versteh mich nicht falsch, ich mag deinen Code und er ist sehr lehrreich, aber je mehr ich solche Konstrukte für einfache Probleme sehe, desto mehr erinnert er mich an den alten "Hallo Welt" -Witz: ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 Stimme von mir).
Greenoldman

4
Wenn Sie Einzeiler schreiben, ist überhaupt nichts wichtig. Wenn Sie wichtige Programme schreiben (groß mit ständigem Wartungs- und Entwicklungsbedarf), führt diese Art des Denkens zu der schnellsten und schädlichsten Art der Verschlechterung der Softwarequalität.
Randall Schulz

3
Nicht jeder wird "Scala-Augen" haben, bis ein gewisses Maß an Übung erreicht ist - es ist lustig zu sehen, dass dieses Codebeispiel von "Beginning" Scala stammt
asyncwait

asyncwait "begin" scala ... der ironischste Titel aller Zeiten, Anmerkung: Ich habe das Buch ... und gerade jetzt fange ich an, es zu verstehen ... Ich nehme an, als ich ein Schritt vor "Anfänger" war lol: D. ........
user1050817

1
Das Problem sind weniger die Scala-Tricks hier, sondern die Ausführlichkeit und der schlechte Stil. Ich habe dies viel besser lesbar bearbeitet. Nach meinem Refactor sind es nur 4 Zeilen (nun, 4 mit IDE-Zeilenlängen, hier werden 6 verwendet, um in den Bildschirm zu passen). IMHO ist es jetzt sehr schöne Antwort.
Samthebest

38

Eine einfache Antwort:

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 könnten Sie die Bibliotheken hinzufügen, importaus denen Sie ?
Daniel

1
Verwenden Sie ab Java 7 stattdessen java.nio.file: def writeToFile (Datei: String, stringToWrite: String): Unit = {val writer = Files.newBufferedWriter (Paths.get (Datei)) Versuchen Sie schließlich writer.write (stringToWrite) writer.close ()}
E Shindler

20

Eine andere Antwort geben, weil meine Änderungen anderer Antworten abgelehnt wurden.

Dies ist die prägnanteste und einfachste Antwort (ähnlich wie bei Garret Hall).

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

Dies ist vergleichbar mit Jus12, aber ohne die Ausführlichkeit und mit dem richtigen Code - Stil

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

Beachten Sie try finally, dass Sie weder die geschweiften Klammern noch Lambdas benötigen , und beachten Sie die Verwendung der Platzhaltersyntax. Beachten Sie auch eine bessere Benennung.


2
Entschuldigung, aber Ihr Code ist vorstellbar, er erfüllt nicht die implementedVoraussetzung. Sie können den nicht implementierten Code nicht verwenden. Ich meine, Sie müssen sagen, wie Sie es finden, da es standardmäßig nicht verfügbar und nicht bekannt ist.
Val

15

Hier ist ein prägnanter Einzeiler, der die Scala-Compiler-Bibliothek verwendet:

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

Wenn Sie die Java-Bibliotheken verwenden möchten, können Sie alternativ diesen Hack ausführen:

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

Was importiert? dh woher kommt die Datei?
Ben Hutchison

Die Scala-Compiler-Bibliothek.
Garrett Hall

3
Nicht mehr lebensfähig (nicht in Scala 2.11)
Brent Faust

1
Worüber redest du? scala.tools.nsc.io.File("/tmp/myFile.txt")funktioniert in Scala 2.11.8.

1
Es ist jetzt in scala.reflect.io.File
Keith Nordstrom

13

Ein Liner zum Speichern / Lesen zu / von String, mit 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
}

Dies ist nicht für große Dateien geeignet, erledigt aber die Aufgabe.

Einige Links:

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


Warum ist das nicht für große Dateien geeignet?
Chetan Bhasin

2
@ChetanBhasin Wahrscheinlich, weil in ein neues Byte-Array writekopiert wird contents, anstatt es in die Datei zu streamen, wodurch auf seinem Höhepunkt doppelt so viel Speicher verbraucht wird als contentsallein.
Daniel Werner

10

Unglücklicherweise für die Top-Antwort ist Scala-IO tot. Wenn es Ihnen nichts ausmacht, eine Abhängigkeit von Drittanbietern zu verwenden, sollten Sie meine OS-Lib-Bibliothek verwenden . Dies macht die Arbeit mit Dateien, Pfaden und dem Dateisystem sehr einfach:

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

Es verfügt über Einzeiler zum Schreiben in Dateien , zum Anhängen an Dateien , zum Überschreiben von Dateien und für viele andere nützliche / allgemeine Vorgänge


Vielen Dank für dieses Update. Upvoted. Ich habe Ihre Antwort oben für mehr Sichtbarkeit in meiner eigenen verwiesen.
VonC

7

Eine Mikrobibliothek, die ich geschrieben habe: https://github.com/pathikrit/better-files

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

oder

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

Auch hier - Diese Frage ist einer der Top-Hits beim Googeln, wie man eine Datei mit Scala schreibt - jetzt, wo Ihr Projekt größer geworden ist, möchten Sie vielleicht Ihre Antwort ein wenig erweitern?
Asac

6

Ab Scala 2.13dem Start bietet die Standardbibliothek ein dediziertes Dienstprogramm zur Ressourcenverwaltung : Using.

Es kann in diesem Fall mit Ressourcen wie PrintWriteroder verwendet werden, BufferedWriterdie erweitert AutoCloseablewerden, um in eine Datei zu schreiben und die Ressource anschließend zu schließen, egal was passiert:

  • Zum Beispiel mit 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)
    }
  • Oder mit 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

UPDATE am 2019 / Sep / 01:

  • Verwenden Sie ab Scala 2.13 lieber scala.util.Using
  • Fehler finallybehoben Exception, durch den das Original verschluckt wurde , trywenn der finallyCode einException

Nachdem ich all diese Antworten zum einfachen Schreiben einer Datei in Scala überprüft hatte und einige davon sehr nett sind, hatte ich drei Probleme:

  1. In der Antwort des Jus12 ist die Verwendung von Currying für die Verwendung der Hilfsmethode für Scala / FP-Anfänger nicht offensichtlich
  2. Muss Fehler niedrigerer Ebene mit kapseln scala.util.Try
  3. Bedürfnisse zu zeigen , Java - Entwickler neue Scala / FP , wie man richtig Nest abhängige Ressourcen , so dass die closeMethode auf jede abhängige Ressource in umgekehrten Reihenfolge - Hinweis: Schließen abhängige Ressourcen in umgekehrter Reihenfolge vor allem im Fall eines Fehlers ist ein selten verstanden Erfordernis Die java.lang.AutoCloseableSpezifikation, die dazu neigt, zu sehr schädlichen und schwer zu findenden Fehlern und Laufzeitfehlern zu führen

Bevor ich anfange, ist mein Ziel nicht prägnant. Dies soll Scala / FP-Anfängern, die normalerweise aus Java stammen, das Verständnis erleichtern. Ganz am Ende werde ich alle Teile zusammenziehen und dann die Prägnanz erhöhen.

Zunächst muss die usingMethode aktualisiert werden, um sie verwenden zu können Try(auch hier ist Prägnanz nicht das Ziel). Es wird umbenannt 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
              }
          }
      }
    )

Der Anfang der obigen tryUsingAutoCloseableMethode kann verwirrend sein, da sie anscheinend zwei Parameterlisten anstelle der üblichen Einzelparameterliste enthält. Dies nennt man Currying. Und ich werde nicht ins Detail gehen, wie Curry funktioniert oder wo es gelegentlich ist nützlich ist. Es stellt sich heraus, dass es für diesen speziellen Problembereich das richtige Werkzeug für den Job ist.

Als nächstes müssen wir eine Methode erstellen tryPrintToFile, die eine erstellt (oder eine vorhandene überschreibt) Fileund eine schreibt List[String]. Es verwendet ein, FileWriterdas von einem eingekapselt BufferedWriterist, das wiederum von einem eingekapselt ist PrintWriter. Um die Leistung zu steigern, wird eine Standardpuffergröße BufferedWriterdefiniert, die viel größer als die Standardpuffergröße ist.defaultBufferSize , und der Wert 65536 zugewiesen.

Hier ist der Code (und auch hier ist Prägnanz nicht das Ziel):

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

Die obige tryPrintToFileMethode ist insofern nützlich, als sie a List[String]als Eingabe nimmt und an a sendet File. Lassen Sie uns nun eine tryWriteToFileMethode erstellen , die a nimmt Stringund in a schreibtFile .

Hier ist der Code (und ich lasse Sie hier die Priorität der Prägnanz erraten):

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

Schließlich ist es nützlich, den Inhalt von a Fileals a abrufen zu können String. Während dies scala.io.Sourceeine bequeme Methode zum einfachen Abrufen des Inhalts von a darstellt File, closemuss die Methode verwendet werden Source, um die zugrunde liegenden JVM- und Dateisystem-Handles freizugeben. Wenn dies nicht erfolgt, wird die Ressource erst freigegeben, wenn der JVM GC (Garbage Collector) die SourceInstanz selbst freigegeben hat. Und selbst dann gibt es nur eine schwache JVM-Garantie, dass die finalizeMethode vom GC zur closeRessource aufgerufen wird . Dies bedeutet, dass es in der Verantwortung des Kunden liegt, das explizit aufzurufen . Dazu benötigen wir eine zweite Definition der using-Methode, die behandelt wird .close Methode , genauso wie es in der Verantwortung eines Kunden liegt, groß zu seinclose eine Instanz von zu ermittelnjava.lang.AutoCloseablescala.io.Source

Hier ist der Code dafür (immer noch nicht präzise):

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

Und hier ist ein Beispiel für die Verwendung in einem supereinfachen Zeilen-Streaming-Dateireader (der derzeit zum Lesen von durch Tabulatoren getrennten Dateien aus der Datenbankausgabe verwendet wird):

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

Eine aktualisierte Version der oben genannten Funktion wurde als Antwort auf eine andere, aber verwandte StackOverflow-Frage bereitgestellt .


Wenn Sie nun alles zusammen mit den extrahierten Importen zusammenführen (was das Einfügen in das Scala-Arbeitsblatt, das sowohl im Eclipse ScalaIDE- als auch im IntelliJ Scala-Plugin vorhanden ist, erheblich vereinfacht, um die Ausgabe auf den Desktop zu übertragen und die Prüfung mit einem Texteditor zu vereinfachen), So sieht der Code aus (mit erhöhter Prägnanz):

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

Als Scala / FP-Neuling habe ich viele Stunden (meistens mit Kopfkratzern) verbrannt, um mir das oben genannte Wissen und die oben genannten Lösungen zu verdienen. Ich hoffe, dies hilft anderen Scala / FP-Neulingen, diesen speziellen Lernbuckel schneller zu überwinden.


2
Unglaubliches Update. Das einzige Problem ist, dass Sie jetzt ungefähr 100 Codezeilen haben, die durch ersetzt werden könnten try-catch-finally. Liebe immer noch deine Leidenschaft.
Beobachter

1
@Observer Ich würde behaupten, dass dies eine ungenaue Aussage ist. Das Muster, das ich beschreibe, reduziert tatsächlich die Menge an Boilerplate, die ein Client schreiben muss, um eine ordnungsgemäße Behandlung des Schließens von AutoCloseables sicherzustellen, und aktiviert gleichzeitig das idiomatische FP-Muster von Scala für die Verwendung von scala.util.Try. Wenn Sie versuchen, die gleichen Effekte zu erzielen, die ich habe, indem Sie die try / catch / finally-Blöcke manuell ausschreiben, werden Sie wahrscheinlich mehr Boilerplate haben, als Sie sich vorstellen. Es gibt also einen signifikanten Lesbarkeitswert, wenn die gesamte Boilerplate in die 100 Zeilen der Scala-Funktion geschoben wird.
chaotic3quilibrium

1
Tut mir leid, wenn das in irgendeiner Weise beleidigend klang. Mein Punkt ist jedoch, dass eine solche Menge an Code nicht erforderlich ist, da dies durch einen nicht funktionalen Ansatz mit viel größerer Einfachheit erreicht werden könnte. Persönlich würde ich try-finally mit einigen zusätzlichen Schecks schreiben. Es ist nur kürzer. Wenn ich Wrapper verwenden wollte, sind ApacheUtils da, um die ganze Drecksarbeit zu nutzen. Darüber hinaus schließen alle Standard-Reader / Writer zugrunde liegende Streams, sodass Ihr Multipwrap nicht benötigt wird. PS: Ich habe meine Stimme von minus eins auf plus eins geändert, um Ihre Bemühungen zu unterstützen. Also, bitte, verdächtige mich nicht in schlechten Absichten.
Beobachter

Keine Beleidigung genommen.
chaotic3quilibrium

1
Ich verstehe Ihren Standpunkt. Danke für die Diskussion, ich muss ein bisschen darüber nachdenken. Einen schönen Tag noch!
Beobachter

3

Hier ist ein Beispiel für das Schreiben einiger Zeilen in eine Datei mit 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

Um Samthebest und die Mitwirkenden vor ihm zu übertreffen, habe ich die Benennung und Prägnanz verbessert:

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

Dies verwendet "Ententypisierung", die von der Reflexion abhängt. Für viele Kontexte ist abhängig von der Reflexion ein Nichtstarter.
chaotic3quilibrium

1

Keine Abhängigkeiten mit Fehlerbehandlung

  • Verwendet ausschließlich Methoden aus der Standardbibliothek
  • Erstellt bei Bedarf Verzeichnisse für die Datei
  • Wird Eitherzur Fehlerbehandlung verwendet

Code

def write(destinationFile: Path, fileContent: String): Either[Exception, Path] =
  write(destinationFile, fileContent.getBytes(StandardCharsets.UTF_8))

def write(destinationFile: Path, fileContent: Array[Byte]): Either[Exception, Path] =
  try {
    Files.createDirectories(destinationFile.getParent)
    // Return the path to the destinationFile if the write is successful
    Right(Files.write(destinationFile, fileContent))
  } catch {
    case exception: Exception => Left(exception)
  }

Verwendung

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

Update 2019:

Zusammenfassung - Java NIO (oder NIO.2 für Async) ist nach wie vor die umfassendste in Scala unterstützte Dateiverarbeitungslösung. Der folgende Code erstellt und schreibt Text in eine neue Datei:

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. Java-Bibliotheken importieren: IO und NIO
  2. Ein ... kreieren Path Objekt mit dem von Ihnen gewählten Dateinamen
  3. Konvertieren Sie Ihren Text, den Sie in eine Datei einfügen möchten, in ein Byte-Array
  4. Holen Sie sich Ihre Datei als Stream: OutputStream
  5. Übergeben Sie Ihr Byte-Array an die writeFunktion Ihres Ausgabestreams
  6. Schließen Sie den Stream

1

Ähnlich wie bei dieser Antwort ist hier ein Beispiel mit 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)

  }
}

0

Diese Zeile hilft beim Schreiben einer Datei aus einem Array oder String.

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

0

Wenn Sie sowieso Akka Streams in Ihrem Projekt haben, bietet es einen Einzeiler:

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

Akka docs> Streaming File IO

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.