Как написать в файл в Scala?

для чтения есть полезная абстракция Source. Как я могу писать строки в текстовый файл?

14 ответов


Edit (сентябрь 2011): since Эдуардо Коста спрашивает о Scala2.9, и с Рик-777 комментарии, что скалакс.IO commit history практически не существует с середины 2009 года...

Scala-IO сменил место: смотри ее GitHub repo С Джесси Eichar (кроме так):

проект зонтика Scala IO состоит из несколько подпроектов для различных аспектов и расширений IO.
Существует два основных компонента Scala IO:

  • базовый - Core в основном занимается чтением и записью данных в произвольные источники и приемники и из них. Черты углового камня Input, Output и Seekable которые обеспечивают основной API.
    Другие классы важности Resource, ReadChars и WriteChars.
  • - файл-это File (под названием Path) API, основанный на комбинации Java 7 NIO filesystem и SBT Pathfinder API.
    Path и FileSystem являются основными точками входа в 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)

оригинальный ответ (январь 2011), со старым местом для scala-io:

если вы не хотите ждать Scala2.9, вы можете использовать Scala-инкубатор / scala-io библиотека.
(как упоминалось в "почему Scala не Источник, близкий базового InputStream?")

посмотреть образцы

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

Это одна из функций, отсутствующих в стандартной Scala, которую я нашел настолько полезной, что добавляю ее в свою личную библиотеку. (Вам, наверное, тоже нужна личная библиотека.) Код выглядит так:

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

и он используется следующим образом:

import java.io._
val data = Array("Five","strings","in","a","file!")
printToFile(new File("example.txt")) { p =>
  data.foreach(p.println)
}

подобно ответу Rex Kerr, но более общий. Сначала я использую вспомогательную функцию:

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

тогда я использую это как:

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

и

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

etc.


простой ответ:

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

давая другой ответ, потому что мои правки других ответов, где отклонены.

Это самый краткий и простой ответ (подобно чердачному Холлу)

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

Это похоже на Jus12, но без многословия и с правильным стиль кода

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

Примечание вам не нужны фигурные скобки для try finally, ни lambdas, и обратите внимание на использование синтаксиса заполнителя. Также обратите внимание на лучшее именование.


один вкладыш для сохранения / чтения в / из String, используя 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
}

Это не подходит для больших файлов, но будет делать эту работу.

ссылки:

java.НИО.файл.Файлы.пиши
java.ленг.Строка.метод getbytes
scala.коллекция.JavaConverters
scala.коллекция.неизменный.Список.mkString


вот краткий однострочный файл с использованием библиотеки компилятора Scala:

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

кроме того, если вы хотите использовать библиотеки Java, вы можете сделать это взломать:

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

С scala запись строки в файл в одном операторе


микро-библиотека я написал:https://github.com/pathikrit/better-files

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

или

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

после просмотра всех этих ответов о том, как легко написать файл в Scala, и некоторые из них довольно приятные, у меня было три вопроса:

  1. на Jus12 это!--45-->, использование карри для использования вспомогательного метода неочевидно для начинающих Scala/FP
  2. необходимо инкапсулировать ошибки нижнего уровня с scala.util.Try
  3. необходимо показать разработчикам Java, новым для Scala / FP, как правильно вложить зависимая ресурсы the close метод выполняется на каждом зависимом ресурсе в обратном порядке -Примечание: закрытие зависимых ресурсов в обратном порядке ОСОБЕННО В СЛУЧАЕ НЕУДАЧИ редко понимаемое требование java.lang.AutoCloseable спецификация, которая, как правило, приводит к очень пагубным и трудно найти ошибки и сбои времени выполнения

прежде чем начать, моя цель - не лаконичность. Это облегчает понимание Scala/FP новички, как правило, те, кто приходит с Java. В самом конце я соберу все кусочки вместе, а затем увеличу краткость.

сначала using метод должен быть обновлен, чтобы использовать Try (опять же, лаконичность-это не цель). Он будет переименован в 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 =>
        try
          transfer(autoCloseable)
        finally
          autoCloseable.close()
    )

начало выше tryUsingAutoCloseable метод может быть запутанным, потому что он, похоже, имеет два списка параметров вместо обычного списка одиночных параметров. Это называется подлизывающийся. И я не буду вдаваться в подробности, как работает карри или где это иногда полезное. Оказывается, для этого конкретного проблемного пространства это правильный инструмент для работы.

Далее, нам нужно создать метод, tryPrintToFile, который создаст (или перезапишет существующий) File и писать List[String]. Он использует FileWriter который инкапсулируется BufferedWriter, который, в свою очередь, инкапсулироваться PrintWriter. И для повышения производительности, размер буфера по умолчанию много больше чем значение по умолчанию для BufferedWriter определяется defaultBufferSize, и присвоено значение 65536.

вот код (и опять же, лаконичность здесь не является целью):

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

выше tryPrintToFile способ полезен тем, что он принимает List[String] в качестве ввода и отправляет его в File. Давайте теперь создадим tryWriteToFile метод, который принимает String и записывает его в File.

вот код (и я позволю вам угадать приоритет краткости здесь):

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

наконец, полезно иметь возможность извлекать содержимое File как String. В то время как scala.io.Source обеспечивает удобный метод для легкого получения содержимого File, the close метод должен использоваться на Source чтобы освободить базовые дескрипторы JVM и файловой системы. Если это не сделано, ресурс не будет выпущен, пока JVM GC (сборщик мусора) не освободит Source сам экземпляр. И даже тогда, есть только слабая гарантия JVM finalize метод будет вызываться GC в close ресурсе. Это означает, что клиент несет ответственность за явное вызов close метод, так же, как это ответственность клиента перед tall close на примере java.lang.AutoCloseable. Для этого нам нужно второе определение, используя метод, который обрабатывает scala.io.Source.

вот код для этого (еще не сжатый):

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

Теги обновленная версия вышеуказанной функции был предоставлен в качестве ответа на другой, но связанный с StackOverflow вопрос.


теперь, приведя все это вместе с извлеченными импортами (что значительно упрощает вставку в рабочий лист Scala, присутствующий как в Eclipse ScalaIDE, так и в IntelliJ Scala плагин, чтобы облегчить сброс вывода на рабочий стол, чтобы его легче было изучить с помощью текстового редактора), вот как выглядит код (с повышенной краткостью):

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

как Новичок Scala/FP, я сжег много часов (в основном голова царапает разочарование), зарабатывая вышеуказанные знания и решения. Я надеюсь, что это поможет другим новичкам Scala/FP быстрее преодолеть этот конкретный учебный горб.


вот пример записи некоторых строк в файл с помощью 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

чтобы превзойти samthebest и участников до него, я улучшил именование и краткость:

  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 для обработки ошибок

код

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

использование

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

эта строка помогает записать файл из массива или строки.

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

Если у вас в любом случае есть потоки Akka в вашем проекте, он предоставляет однострочный:

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

Akka docs > потоковый файл IO