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

138

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

  • 1
    Если вы знаете, как это сделать в Java, то вы можете использовать то же самое в Scala. Ваш вопрос относится к стандартной библиотеке Scala?
  • 1
    @wheaties да лучший способ сделать это в Scala
Показать ещё 1 комментарий
Теги:
file-io
scala-2.8

14 ответов

64
Лучший ответ

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

Scala-IO изменилось: см. GitHub repo, от Джесси Эйхар (также на SO):

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

  • Core. Основное внимание в основном уделяется чтению и записи данных из произвольных источников и стоков. Каменные черты угла - Input, Output и Seekable, которые предоставляют базовый API.
    Другими важными классами являются Resource, ReadChars и WriteChars.
  • Файл. Файл - это File (называемый Path) API, основанный на комбинации файловой системы Java 7 NIO и API-интерфейсов SBT PathFinder.
    Path и FileSystem являются основными точками входа в API файлов Scala IO.
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 -incubator/scala -io библиотека.
(как упоминалось в "Почему не Scala Source закрывает базовый 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)
  }
  • 15
    А как насчет версии Scala 2.9? :)
  • 0
    Проект scalax кажется мертвым (с июня 2009 года нет коммитов). Это правильно? скалярная фиксация истории
Показать ещё 5 комментариев
198

Это одна из функций, отсутствующих в стандартном 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)
}
  • 1
    new java.io.PrintWriter () использует кодировку платформы по умолчанию, что, вероятно, означает, что файл результатов не очень переносим. Например, если вы хотите создать файл, который позже можете отправить по электронной почте, вам, вероятно, следует использовать конструктор PrintWriter, который позволяет указать кодировку.
  • 0
    @JonaChristopherSahnwaldt - Конечно, в особых случаях вы можете указать кодировку. Значение по умолчанию для платформы - это наиболее разумное значение по умолчанию в среднем. То же, что и с Source (кодировка по умолчанию по умолчанию). Конечно, вы можете добавить, например, параметр enc: Option[String] = None после f если вам это необходимо.
Показать ещё 4 комментария
46

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

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

и др.

  • 37
    Не поймите меня неправильно, мне нравится ваш код, и он очень познавательный, но чем больше я вижу такие конструкции для простых задач, тем больше он напоминает мне о старой шутке «Привет, мир»: ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-) (+1 голос от меня).
  • 3
    Если ты пишешь одну строку, ничего не имеет значения. Если вы пишете важные программы (большие с постоянной потребностью в обслуживании и развитии), этот тип мышления приводит к наиболее быстрому и пагубному ухудшению качества программного обеспечения.
Показать ещё 7 комментариев
34

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

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 Можете ли вы добавить библиотеки, из которых вы import ?
  • 2
    Первый ответ в списке, который не перестал существовать с 2015 года ...
Показать ещё 1 комментарий
20

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

Это самый сжатый и простой ответ (похожий на Garret Hall's)

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

  • 2
    Извините, но ваш код мыслимый, он не соответствует implemented условию. Вы не можете использовать код, который не реализован. Я имею в виду, что вы должны сказать, как его найти, так как он не доступен по умолчанию и малоизвестен.
13

Один вкладыш для сохранения/чтения в/из 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.nio.file.Files.write
java.lang.String.getBytes
scala.collection.JavaConverters
scala.collection.immutable.List.mkString

  • 0
    Почему это не подходит для больших файлов?
  • 2
    @ChetanBhasin Вероятно, потому что write будет копировать contents в новый байтовый массив вместо потоковой передачи его в файл, таким образом, на своем пике он использует вдвое больше памяти, чем только contents .
13

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

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

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

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

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

  • 0
    Какой импорт? то есть откуда приходит файл?
  • 0
    Библиотека компилятора Scala.
Показать ещё 4 комментария
5

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

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

или

file << "Hello" << "\n" << "World"
  • 0
    И здесь - этот вопрос является одним из самых популярных при поиске, как написать файл с scala - теперь, когда ваш проект стал больше, возможно, вы захотите немного расширить свой ответ?
4

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

  • В ответе Jus12 использование currying для использования вспомогательного метода неочевидно для Scala/FP beginners
  • Необходимо инкапсулировать ошибки нижнего уровня с помощью scala.util.Try
  • Нужно показать разработчикам 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 и напишет a List[String]. Он использует FileWriter, который инкапсулирован a 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 обеспечивает удобный способ для легкого получения содержимого a File, метод close должен использоваться в Source для освобождения основных JVM и дескрипторов файловой системы. Если этого не сделать, ресурс не будет выпущен до тех пор, пока JVM GC (Garbage Collector) не начнет выпускать сам экземпляр Source. И даже тогда, существует только слабая гарантия JVM, метод finalize будет вызываться GC для close ресурса. Это означает, что ответственность клиента за явное обращение к методу close заключается в том, что клиент отвечает на высокий 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
      )
  )

обновленная версия вышеуказанной функции была предоставлена ​​в качестве ответа на https://stackoverflow.com/questions/501090/how-do-i-convert-a-decimal-to-an-int-in-c.


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

  • 1
    Невероятное обновление. Единственная проблема в том, что теперь у вас есть около 100 строк кода, которые можно заменить на try-catch-finally . Все еще любите свою страсть.
  • 1
    @ Observer Я бы сказал, что это неточное утверждение. Шаблон, который я описываю, на самом деле сокращает количество шаблонов, которые клиент должен написать, чтобы обеспечить правильную обработку закрытия AutoCloseables и одновременно включить идиоматический шаблон Scala FP с использованием scala.util.Try. Если вы попытаетесь добиться того же эффекта, что и я, вручную выписав блоки try / catch / finally, я думаю, вы обнаружите, что в итоге вы получите гораздо больше шаблонов, чем вы себе представляете. Таким образом, существует значительная читаемость при вставке всего шаблона в 100 строк функции Scala.
Показать ещё 5 комментариев
2

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

Без зависимостей, с обработкой ошибок

  • Использует только методы из стандартной библиотеки
  • Создает каталоги для файла, если необходимо
  • Использует 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")
}
1

Чтобы превзойти 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))
  • 0
    Это использует "типизацию утки", которая зависит от отражения. Для многих контекстов зависимость от отражения не является началом.
0

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

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

Akka docs> Streaming File IO

0

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню