scala 如何在Scala中写入文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4604237/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:41:54  来源:igfitidea点击:

How to write to a file in Scala?

scalafile-ioscala-2.8

提问by yura

For reading, there is the useful abstraction Source. How can I write lines to a text file?

对于阅读,有一个有用的抽象Source。如何将行写入文本文件?

采纳答案by VonC

Edit 2019 (8 years later), Scala-IObeing not very active, if any, Li Haoyisuggests his own library lihaoyi/os-lib, that he presents below.

编辑 2019(8 年后),Scala-IO不是很活跃,如果有的话,李浩义建议他自己的库lihaoyi/os-lib,他在下面介绍

June 2019, Xavier Guihotmentions in his answerthe library Using, a utility for performing automatic resource management.

2019 年 6 月,Xavier Guihot他的回答中提到了 library Using,这是一个用于执行自动资源管理的实用程序。



Edit (September 2011): since Eduardo Costaasks about Scala2.9, and since Rick-777comments that scalax.IO commit historyis pretty much non-existent since mid-2009...

编辑(2011 年 9 月):由于Eduardo Costa询问了 Scala2.9,并且因为Rick-777评论说scalax.IO 提交历史自 2009 年年中以来几乎不存在......

Scala-IOhas changed place: see its GitHub repo, from Jesse Eichar(also on SO):

斯卡拉-IO已经改变的地方:看它的GitHub库,从杰西Eichar(也对SO):

The Scala IO umbrella project consists of a few sub projects for different aspects and extensions of IO.
There are two main components of Scala IO:

  • Core- Core primarily deals with Reading and writing data to and from arbitrary sources and sinks. The corner stone traits are Input, Outputand Seekablewhich provide the core API.
    Other classes of importance are Resource, ReadCharsand WriteChars.
  • File- File is a File(called Path) API that is based on a combination of Java 7 NIO filesystem and SBT PathFinder APIs.
    Pathand FileSystemare the main entry points into the Scala IO File API.

Scala IO 伞形项目由几个子项目组成,用于 IO 的不同方面和扩展。
Scala IO 有两个主要组件:

  • 核心- 核心主要处理从任意源和接收器读取和写入数据。基石特性是Input,OutputSeekable提供核心 API。
    其他重要的类别是ResourceReadCharsWriteChars
  • 文件- 文件是一个File(称为Path)API,它基于 Java 7 NIO 文件系统和 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)


Original answer (January 2011), with the old place for scala-io:

原始答案(2011 年 1 月),scala-io 的旧位置:

If you don't want to wait for Scala2.9, you can use the scala-incubator / scala-iolibrary.
(as mentioned in "Why doesn't Scala Source close the underlying InputStream?")

如果不想等待Scala2.9,可以使用scala-incubator / scala-io库。
(如“为什么 Scala Source 不关闭底层 InputStream?”中所述)

See the samples

查看样品

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

回答by Rex Kerr

This is one of the features missing from standard Scala that I have found so useful that I add it to my personal library. (You probably should have a personal library, too.) The code goes like so:

这是标准 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() }
}

and it's used like this:

它是这样使用的:

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

回答by Jus12

Similar to the answer by Rex Kerr, but more generic. First I use a helper function:

类似于 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() }

Then I use this as:

然后我将其用作:

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

and

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

etc.

等等。

回答by samthebest

A simple answer:

一个简单的答案:

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

回答by samthebest

Giving another answer, because my edits of other answers where rejected.

给出另一个答案,因为我对其他答案的编辑被拒绝了。

This is the most concise and simple answer(similar to Garret Hall's)

这是最简洁最简单的答案(类似于Garret Hall的)

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

This is similar to Jus12, but without the verbosity and with correct code style

这类似于 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))

Note you do NOT need the curly braces for try finally, nor lambdas, and note usage of placeholder syntax. Also note better naming.

请注意,您不需要大括号 for try finally,也不需要 lambda,并注意占位符语法的用法。还要注意更好的命名。

回答by Garrett Hall

Here is a concise one-liner using the Scala compiler library:

这是使用 Scala 编译器库的简洁单行代码:

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

Alternatively, if you want to use the Java libraries you can do this hack:

或者,如果您想使用 Java 库,您可以执行以下操作:

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

回答by Nick Zalutskiy

One liners for saving/reading to/from String, using java.nio.

一个用于保存/读取到/从的衬垫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
}

This isn't suitable for large files, but will do the job.

这不适合大文件,但可以完成这项工作。

Some links:

一些链接:

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

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

回答by Li Haoyi

Unfortunately for the top answer, Scala-IO is dead. If you don't mind using a third-party dependency, consider using my OS-Lib library. This makes working with files, paths and the filesystem very easy:

不幸的是,对于最佳答案,Scala-IO 已经死了。如果您不介意使用第三方依赖项,请考虑使用我的OS-Lib 库。这使得处理文件、路径和文件系统变得非常容易:

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

It has one-liners for writing to files, appending to files, overwriting files, and many other useful/common operations

它具有用于写入文件附加到文件覆盖文件以及许多其他有用/常见操作的单行程序

回答by pathikrit

A micro library I wrote: https://github.com/pathikrit/better-files

我写的一个微库:https: //github.com/pathikrit/better-files

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

or

或者

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

回答by chaotic3quilibrium

UPDATE on 2019/Sep/01:

2019 年 9 月 1 日更新:

  • Starting with Scala 2.13, prefer using scala.util.Using
  • Fixed bug where finallywould swallow original Exceptionthrown by tryif finallycode threw an Exception
  • 从 Scala 2.13 开始,更喜欢使用scala.util.Using
  • 修复了如果代码抛出一个finally会吞下原始Exception抛出的错误tryfinallyException


After reviewing all of these answers on how to easily write a file in Scala, and some of them are quite nice, I had three issues:

在查看了有关如何在 Scala 中轻松编写文件的所有这些答案后,其中一些非常好,我遇到了三个问题:

  1. In the Jus12's answer, the use of currying for the using helper method is non-obvious for Scala/FP beginners
  2. Needs to encapsulate lower level errors with scala.util.Try
  3. Needs to show Java developers new to Scala/FP how to properly nest dependentresources so the closemethod is performed on each dependent resource in reverse order - Note:closing dependent resources in reverse order ESPECIALLY IN THE EVENT OF A FAILUREis a rarely understood requirement of the java.lang.AutoCloseablespecification which tends to lead to very pernicious and difficult to find bugs and run time failures
  1. Jus12 的回答中,对于 Scala/FP 初学者来说,使用辅助方法使用柯里化是不明显的
  2. 需要封装较低级别的错误 scala.util.Try
  3. 需要向不熟悉 Scala/FP 的 Java 开发人员展示如何正确嵌套依赖资源,以便close以相反的顺序在每个依赖资源上执行该方法 -注意:以相反的顺序关闭依赖资源,特别是在 FAILURE 的情况下是很少理解的要求java.lang.AutoCloseable往往会导致非常有害且难以发现错误和运行时故障的规范

Before starting, my goal isn't conciseness. It's to facilitate easier understanding for Scala/FP beginners, typically those coming from Java. At the very end, I will pull all the bits together, and then increase the conciseness.

在开始之前,我的目标不是简洁。这是为了让 Scala/FP 初学者更容易理解,通常是那些来自 Java 的初学者。最后,我会把所有的点放在一起,然后增加简洁性。

First, the usingmethod needs to be updated to use Try(again, conciseness is not the goal here). It will be renamed to tryUsingAutoCloseable:

首先,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 => {
        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
              }
          }
      }
    )

The beginning of the above tryUsingAutoCloseablemethod might be confusing because it appears to have two parameter lists instead of the customary single parameter list. This is called currying. And I won't go into detail how currying works or where it is occasionallyuseful. It turns out that for this particular problem space, it's the right tool for the job.

上述tryUsingAutoCloseable方法的开头可能会令人困惑,因为它似乎有两个参数列表,而不是习惯的单参数列表。这称为柯里化。我不会详细介绍柯里化的工作原理或它偶尔有用的地方。事实证明,对于这个特定的问题空间,它是适合这项工作的工具。

Next, we need to create method, tryPrintToFile, which will create a (or overwrite an existing) Fileand write a List[String]. It uses a FileWriterwhich is encapsulated by a BufferedWriterwhich is in turn encapsulated by a PrintWriter. And to elevate performance, a default buffer size much larger than the default for BufferedWriteris defined, defaultBufferSize, and assigned the value 65536.

接下来,我们需要创建方法,tryPrintToFile,它将创建一个(或覆盖一个现有的)File并写入一个List[String]. 它使用FileWriter由 a 封装的 a BufferedWriter,而后者又由 a 封装PrintWriter。和提升性能,默认缓冲区大小比默认为大得多的BufferedWriter定义,defaultBufferSize以及分配的值65536。

Here's the code (and again, conciseness is not the goal here):

这是代码(同样,简洁不是这里的目标):

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

The above tryPrintToFilemethod is useful in that it takes a List[String]as input and sends it to a File. Let's now create a tryWriteToFilemethod which takes a Stringand writes it to a File.

上述tryPrintToFile方法很有用,因为它将 aList[String]作为输入并将其发送到 a File。现在让我们创建一个tryWriteToFile接受 aString并将其写入a 的方法File

Here's the code (and I'll let you guess conciseness's priority here):

这是代码(我会让你在这里猜测简洁的优先级):

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

Finally, it is useful to be able to fetch the contents of a Fileas a String. While scala.io.Sourceprovides a convenient method for easily obtaining the contents of a File, the closemethod must be used on the Sourceto release the underlying JVM and file system handles. If this isn't done, then the resource isn't released until the JVM GC (Garbage Collector) gets around to releasing the Sourceinstance itself. And even then, there is only a weak JVM guarantee the finalizemethod will be called by the GC to closethe resource. This means that it is the client's responsibility to explicitly call the closemethod, just the same as it is the responsibility of a client to tall closeon an instance of java.lang.AutoCloseable. For this, we need a second definition of the using method which handles scala.io.Source.

最后,能够将 a 的内容File作为 a获取是很有用的String。虽然scala.io.Source提供了一种方便的方法来轻松获取 a 的内容File,但close必须在 上使用该方法才能Source释放底层 JVM 和文件系统句柄。如果没有这样做,那么在 JVM GC(垃圾收集器)开始释放Source实例本身之前,不会释放资源。即便如此,只有一个弱 JVM 保证该finalize方法将被 GC 调用到close资源。这意味着显式调用close方法是客户端的责任,就像客户端有责任closejava.lang.AutoCloseable. 为此,我们需要处理scala.io.Source.

Here's the code for this (still not being concise):

这是这个的代码(仍然不简洁):

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

And here is an example usage of it in a super simple line streaming file reader (currently using to read tab-delimited files from database output):

这是它在超级简单的行流文件阅读器中的示例用法(目前用于从数据库输出中读取制表符分隔的文件):

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 updated version of the above functionhas been provided as an answer to a different but related StackOverflow question.

已提供上述函数更新版本作为对不同但相关的 StackOverflow 问题的回答



Now, bringing that all together with the imports extracted (making it much easier to paste into Scala Worksheet present in both Eclipse ScalaIDE and IntelliJ Scala plugin to make it easy to dump output to the desktop to be more easily examined with a text editor), this is what the code looks like (with increased conciseness):

现在,将所有这些与提取的导入结合在一起(更容易粘贴到 Eclipse ScalaIDE 和 IntelliJ Scala 插件中的 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) //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
    )
  }

As a Scala/FP newbie, I've burned many hours (in mostly head-scratching frustration) earning the above knowledge and solutions. I hope this helps other Scala/FP newbies get over this particular learning hump faster.

作为 Scala/FP 的新手,我已经花了很多时间(主要是令人头疼的挫败感)来获得上述知识和解决方案。我希望这可以帮助其他 Scala/FP 新手更快地克服这个特殊的学习障碍。