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


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

14 145

14 ответов:

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

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

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

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

похоже на ответ Рекса Керра, но более общий. Сначала я использую вспомогательную функцию:

/**
 * 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, ни лямбды, и обратите внимание на использование синтаксиса заполнителя. Также обратите внимание на лучшее именование.

один лайнер для сохранения / чтения в/из 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
скала.коллекция.JavaConverters
скала.коллекция.неизменный.Список.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, использование карринга для использования вспомогательного метода неочевидно для начинающих Scala/FP
  2. необходимо инкапсулировать ошибки нижнего уровня с помощью scala.util.Try
  3. необходимо показать разработчикам Java, новым для Scala / FP, как правильно вложить зависимая ресурсы элемент 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 который в свою очередь инкапсулируется a 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 на close метод должен быть использован на Source чтобы освободить базовые дескрипторы JVM и файловой системы. Если это не сделано, то ресурс не освобождается до тех пор, пока JVM GC (сборщик мусора) не приступит к освобождению Source сам экземпляр. И даже тогда, есть только слабая гарантия JVM finalize метод будет вызван GC to 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
      )
  )

An обновленная версия вышеуказанной функции был предоставлен в качестве ответа на A другой, но связанный с 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 > файл потокового ввода-вывода