wake-up-neo.net

Wie schreibe ich eine Datei in Scala?

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

148
yura

Edit (September 2011): da Eduardo Costa nach Scala2.9 fragt, und seit Rick-777 Kommentaren, dass scalax.IO Commit History so ziemlich nichts seit Mitte 2009 existent ...

SCALA-IO hat sich geändert: siehe GitHub-Repo , von Jesse Eichar (auch auf SO) ):

Das Dachprojekt Scala IO 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 in und aus beliebigen Quellen und Senken. Die Ecksteinmerkmale sind Input, Output und Seekable, die die Kern-API bereitstellen.
    Andere wichtige Klassen sind Resource, ReadChars und WriteChars.
  • File - File ist eine File-API (genannt Path), die auf einer Kombination von Java 7 NIO-Dateisystem und SBT PathFinder-APIs basiert.
    Path und FileSystem sind die Haupteinstiegspunkte in die Scala IO -Datei-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 Bibliothek scala-inkubator/scala-io verwenden.
(wie unter " Warum schließt Scala Source den zugrundeliegenden InputStream nicht schließen?" ")

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

Dies ist eine der Funktionen, die in der Standard-Scala fehlen, 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 lautet wie folgt:

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)
}
208
Rex Kerr

Ähnlich der Antwort von Rex Kerr, aber generischer. 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 das 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)
    }
  }

usw.

49
Jus12

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

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

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

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

Dies ist ähnlich wie bei Jus12, jedoch ohne die Ausführlichkeit und mit korrektem code style

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, dass Sie weder die geschweiften Klammern für try finally noch für Lambdas benötigen. Beachten Sie auch die Verwendung der Platzhaltersyntax. Beachten Sie auch eine bessere Benennung.

20
samthebest

Ein Liner zum Speichern/Lesen in/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

13
Nick Zalutskiy

Hier ist ein kompakter Einzeiler mit der Compiler-Bibliothek Scala:

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

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

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

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

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

oder

file << "Hello" << "\n" << "World"
6
pathikrit

Nachdem ich all diese Antworten darauf gelesen hatte, wie man auf einfache Weise eine Datei in Scala schreibt, und einige davon sind ziemlich gut, hatte ich drei Probleme:

  1. In der Antwort von Jus12 ist die Verwendung von Currying für die Using-Helper-Methode für Scala/FP-Anfänger nicht offensichtlich
  2. Muss untergeordnete Fehler mit scala.util.Try Kapseln
  3. Es muss den Java in Scala/FP neuen Entwicklern gezeigt werden, wie sie abhängig Ressourcen richtig verschachteln, damit die close -Methode für jede abhängige Ressource in ausgeführt wird umgekehrte Reihenfolge - Hinweis: Schließen abhängiger Ressourcen in umgekehrter Reihenfolge INSBESONDERE IM FALL EINES AUSFALLS ist eine selten verstandene Anforderung der Java.lang.AutoCloseable Spezifikation, die dazu neigt, sehr schädliche und schwer auffindbare Fehler und Laufzeitfehler zu verursachen

Bevor ich anfange, ist mein Ziel nicht präzise. Dies erleichtert das Verständnis für Scala/FP-Anfänger, die normalerweise aus Java stammen. Ganz am Ende werde ich alle Teile zusammenziehen und dann die Prägnanz erhöhen.

Erstens muss die using -Methode aktualisiert werden, um Try zu verwenden (auch hier ist Prägnanz nicht das Ziel). Es wird in tryUsingAutoCloseable umbenannt:

def tryUsingAutoCloseable[A <: AutoCloseable, R]
  (instantiateAutoCloseable: () => A) //parameter list 1
  (transfer: A => scala.util.Try[R])  //parameter list 2
: scala.util.Try[R] =
  Try(instantiateAutoCloseable())
    .flatMap(
      autoCloseable =>
        try
          transfer(autoCloseable)
        finally
          autoCloseable.close()
    )

Der Anfang der obigen tryUsingAutoCloseable -Methode kann verwirrend sein, da anscheinend zwei Parameterlisten anstelle der üblichen Einzelparameterliste vorhanden sind. Das nennt man Curry. Und ich werde nicht ins Detail gehen, wie Currying funktioniert oder wo es gelegentlich 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 die Methode tryPrintToFile erstellen, die eine File erstellt (oder eine vorhandene überschreibt) und eine List[String] Schreibt. Es wird ein FileWriter verwendet, das von einem BufferedWriter eingekapselt wird, das seinerseits von einem PrintWriter eingekapselt wird. Um die Leistung zu steigern, wird eine Standardpuffergröße definiert, die viel größer ist als die Standardgröße für BufferedWriter, 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 tryPrintToFile -Methode ist nützlich, da sie einen List[String] Als Eingabe verwendet und an einen File sendet. Erstellen wir nun eine tryWriteToFile -Methode, die eine String aufnimmt und in eine File schreibt.

Hier ist der Code (und ich lasse Sie die Priorität der Prägnanz hier 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 eines File als String abrufen zu können. Während scala.io.Source Eine bequeme Methode zum einfachen Abrufen des Inhalts von File bietet, muss die close -Methode für Source verwendet werden, um die zugrunde liegende JVM und freizugeben Dateisystem-Handles. Andernfalls wird die Ressource erst freigegeben, wenn der JVM GC (Garbage Collector) die Instanz Source selbst freigibt. Und selbst dann gibt es nur eine schwache JVM-Garantie, dass die finalize -Methode vom GC zum close der Ressource aufgerufen wird. Dies bedeutet, dass es in der Verantwortung des Clients liegt, die close -Methode explizit aufzurufen, genauso wie es in der Verantwortung eines Clients liegt, close für eine Instanz von Java.lang.AutoCloseable Zu berechnen. Dazu benötigen wir eine zweite Definition der using-Methode, die scala.io.Source Behandelt.

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

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)
  (transfer: S => scala.util.Try[R])
: scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source =>
        try
          transfer(source))
        finally
          source.close()
    )

Und hier ist ein Beispiel für die Verwendung in einem supereinfachen Zeilen-Streaming-Dateireader (der derzeit zum Lesen tabulatorgetrennter 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 obigen Funktion wurde als Antwort auf eine andere, aber verwandte StackOverflow-Frage bereitgestellt.


Nun bringen wir das alles zusammen mit den extrahierten Importen (was das Einfügen in das Scala Arbeitsblatt, das sowohl in Eclipse ScalaIDE als auch im IntelliJ Scala Plugin vorhanden ist, um es zu erstellen, viel einfacher macht) Die Ausgabe kann einfach auf den Desktop kopiert werden, um sie mit einem Texteditor besser untersuchen zu können. 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)(transfer: A => scala.util.Try[R]): scala.util.Try[R] =
  Try(instantiateAutoCloseable())
    .flatMap(
      autoCloseable =>
        try transfer(autoCloseable)) finally autoCloseable.close()
    )

def tryUsingSource[S <: scala.io.Source, R]
  (instantiateSource: () => S)(transfer: S => scala.util.Try[R]): scala.util.Try[R] =
  Try(instantiateSource())
    .flatMap(
      source =>
        try transfer(source)) finally source.close()
    )

def tryPrintToFile(
  lines: List[String],
  location: File,
  bufferSize: Int = defaultBufferSize
): Try[Unit] =
  tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter =>
    tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter =>
      tryUsingAutoCloseable(() => new PrintWriter(bufferedWriter)) { printWriter =>
          Try(lines.foreach(line => printWriter.println(line)))
      }
    }
  }

def tryWriteToFile(
  content: String,
  location: File,
  bufferSize: Int = defaultBufferSize
): Try[Unit] =
  tryUsingAutoCloseable(() => new FileWriter(location)) { fileWriter =>
    tryUsingAutoCloseable(() => new BufferedWriter(fileWriter, bufferSize)) { bufferedWriter =>
      Try(bufferedWriter.write(content))
    }
  }

def tryProcessSource(
    file: File,
  parseLine: (String, Int) => List[String] = (line, index) => List(line),
  filterLine: (List[String], Int) => Boolean = (values, index) => true,
  retainValues: (List[String], Int) => List[String] = (values, index) => values,
  isFirstLineNotHeader: Boolean = false
): Try[List[List[String]]] =
  tryUsingSource(Source.fromFile(file)) { source =>
    Try(
      ( for {
          (line, index) <- source.getLines().buffered.zipWithIndex
          values = parseLine(line, index)
          if (index == 0 && !isFirstLineNotHeader) || filterLine(values, index)
          retainedValues = retainValues(values, index)
        } yield retainedValues
      ).toList
    )
  )

Als Scala/FP-Neuling habe ich viele Stunden (vor allem in der Kopfkratzer-Frustration) damit verbracht, die oben genannten Kenntnisse und Lösungen zu erlangen. Ich hoffe, dass dies anderen Scala/FP-Neulingen hilft, diesen bestimmten Lernhügel schneller zu überwinden.

5

Hier ein Beispiel zum 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
2
Chris Martin

Keine Abhängigkeiten mit Fehlerbehandlung

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

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

Verwendungszweck

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
Matthias Braun

Update 2019:

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

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. Importieren Sie Java-Bibliotheken: IO und NIO
  2. Erstellen Sie ein Path Objekt mit dem von Ihnen gewählten Dateinamen
  3. Konvertieren Sie den 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 Bytearray an die Funktion write Ihres Ausgabestreams
  6. Schließen Sie den Stream
1
Janac Meena

Leider ist Scala-IO für die beste Antwort tot. Wenn es Ihnen nichts ausmacht, eine Drittanbieter-Abhängigkeit zu verwenden, ziehen Sie die Verwendung von my OS-Lib library in Betracht. Dies macht das Arbeiten 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 gibt Einzeiler für Schreiben in Dateien , Anhängen an Dateien , Überschreiben von Dateien und viele andere nützliche/häufige Vorgänge

1
Li Haoyi

Ähnlich wie diese 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)

  }
}
1
Valy Dia

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

Wenn Sie 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-Dokumente> Streaming-Datei IO

0
akauppi

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

Es kann in diesem Fall mit Ressourcen wie PrintWriter oder BufferedWriter verwendet werden, die AutoCloseable erweitern, um in eine Datei zu schreiben und die Ressource anschließend zu schließen:

  • Zum Beispiel mit Java.io api:

    import scala.util.Using
    import Java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
    // scala.util.Try[Unit] = Success(())
    
  • Oder mit Java.nio api:

    import scala.util.Using
    import Java.nio.file.{Files, Paths}, Java.nio.charset.Charset
    
    // val lines = List("hello", "world")
    Using(Files.newBufferedWriter(Paths.get("file.txt"), Charset.forName("UTF-8"))) {
      writer => lines.foreach(line => writer.write(line + "\n"))
    }
    // scala.util.Try[Unit] = Success(())
    
0
Xavier Guihot

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

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