如何在Scala中写入文件?

yur*_*ura 153 file-io scala scala-2.8

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

Rex*_*err 209

这是标准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() }
}
Run Code Online (Sandbox Code Playgroud)

它的使用方式如下:

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

  • @RexKerr - 我不同意.在几乎所有情况下都应该指定编码.我遇到的大多数编码错误都是因为人们不理解或不考虑编码而发生的.他们使用默认设置,甚至不知道它,因为太多的API让他们逃脱了它.如今,最明智的默认值可能是UTF-8.也许您只使用英语和其他可以用ASCII编写的语言.幸运的你.我住在德国,不得不修复比我记忆中更多破碎的变音符号. (6认同)
  • @JonaChristopherSahnwaldt - 这是一个合理的默认编码的原因,而不是强迫每个人一直指定它.但是如果你在Mac上并且你用Java编写的文件是gobbledygook,因为它们不是Mac OS罗马编码的,我不确定它做得多好而不是伤害.我认为这是平台的错,他们没有就字符集达成一致.作为一个单独的开发人员,键入字符串实际上不会解决问题.(所有开发者都会同意UTF-8,但那可以作为默认值.) (3认同)

Von*_*onC 70

编辑(2011年9月):自从Eduardo Costa询问Scala2.9以来,自从Rick-777评论scalax.IO承诺历史以来,自2009年中期以来几乎不存在......

Scala-IO改变了地方:从 Jesse Eichar(也在 SO上)看到它的 GitHub回购:

Scala IO总体项目包含一些针对IO的不同方面和扩展的子项目.
Scala IO有两个主要组件:

  • 核心 - 核心主要处理从任意源和接收器读取和写入数据.基石特质lihaoyi/os-lib,Using以及Input其提供的核心API.
    其他重要的类别是Output,SeekableResource.
  • 文件 - 文件是一种ReadChars(称为WriteChars)API,它基于Java 7 NIO文件系统和SBT PathFinder API的组合.
    File并且Path是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)
Run Code Online (Sandbox Code Playgroud)

原始答案(2011年1月),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)
  }
Run Code Online (Sandbox Code Playgroud)

  • Scala 2.9版本怎么样?:) (15认同)
  • 这真的是Scala 2.10的当前建议吗?使用Scala IO?Scala核心中没有任何东西吗? (10认同)
  • 我从来没有使用过scalax.io,但从这些示例行来看,似乎它的API设计非常糟糕.在一个界面中混合字符和二进制数据的方法没有多大意义,很可能导致编码很难找到的错误.java.io(Reader/Writer与InputStream/OutputStream)的设计似乎要好得多. (2认同)

Jus*_*s12 49

类似于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() }
Run Code Online (Sandbox Code Playgroud)

然后我用它作为:

def writeToFile(fileName:String, data:String) = 
  using (new FileWriter(fileName)) {
    fileWriter => fileWriter.write(data)
  }
Run Code Online (Sandbox Code Playgroud)

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

等等

  • 不要误会我的意思,我喜欢你的代码而且它非常有教育意义,但是我越是看到这种简单问题的结构,就越能让我想起旧的"你好世界"的笑话:http://www.ariel.com .au/jokes/The_Evolution_of_a_Programmer.html :-)(来自我的+1票). (39认同)
  • 如果你正在编写单行内容,那就什么都不重要了.如果您正在编写重要的程序(大型程序需要维护和演进),这种思维会导致最快速和有害的软件质量下降. (4认同)
  • 在某种程度的练习之前,不是每个人都会有"scala眼睛" - 看到这个代码示例来自"Beginning"Scala很有趣 (3认同)

sam*_*est 35

一个简单的答案:

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()
  }
Run Code Online (Sandbox Code Playgroud)

  • 从 java 7 开始,使用 java.nio.file 代替: def writeToFile(file: String, stringToWrite: String): Unit = { val writer = Files.newBufferedWriter(Paths.get(file)) try writer.write(stringToWrite) 最后writer.close() } (2认同)

sam*_*est 20

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

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

File("filename").writeAll("hello world")
Run Code Online (Sandbox Code Playgroud)

这类似于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))
Run Code Online (Sandbox Code Playgroud)

请注意,您不需要花括号try finally,也不需要lambdas,并注意占位符语法的使用.还要注意更好的命名.

  • 对不起,但您的代码是可以想象的,它不符合`implements`先决条件.您不能使用未实现的代码.我的意思是你必须告诉如何找到它,因为它默认不可用而且不为人所知. (2认同)

Gar*_*all 13

这是一个使用Scala编译器库的简洁单行程序:

__CODE__

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

__CODE__

scala在一个语句中将字符串写入文件

  • 我想知道为什么他们总是删除最好的实现? (4认同)
  • 不再可行(不在Scala 2.11中) (3认同)

Nic*_*kiy 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
}
Run Code Online (Sandbox Code Playgroud)

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

一些链接:

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

  • @ChetanBhasin可能是因为`write`会将`contents`复制到一个新的字节数组而不是将它流式传输到文件中,从而在其峰值时使用的内存比单独的`contents`多两倍. (2认同)

Xav*_*hot 9

开始Scala 2.13,标准库提供了一个专用的资源管理实用程序:Using.

在这种情况下,它可以与资源一起使用,例如PrintWriterBufferedWriter扩展AutoCloseable以写入文件,无论如何,之后关闭资源:

  • 例如,使用java.ioapi:

    import scala.util.Using, java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 或者使用java.nioapi:

    import scala.util.Using, 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"))
    }
    
    Run Code Online (Sandbox Code Playgroud)


pat*_*rit 7

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

file.appendLine("Hello", "World")
Run Code Online (Sandbox Code Playgroud)

要么

file << "Hello" << "\n" << "World"
Run Code Online (Sandbox Code Playgroud)


cha*_*ium 6

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

  1. Jus12的答案中,对于Scala/FP初学者来说,使用currying来使用辅助方法是不明显的
  2. 需要封装较低级别的错误 finally
  3. 需要向Java开发人员展示新的Scala/FP如何正确嵌套依赖资源,以便Exception以相反的顺序对每个依赖资源执行该方法 - 注意:以相反顺序关闭依赖资源特别是在失败的情况下很少被理解为在try这往往会导致非常有害的,难以规范,发现问题和运行时的故障

在开始之前,我的目标不是简洁.这有助于更容易理解Scala/FP初学者,通常来自Java.在最后,我将把所有的位拉到一起,然后增加简洁性.

首先,finally需要更新该方法以使用Exception(同样,简洁性不是此处的目标).它将被重命名为scala.util.Try:

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
              }
          }
      }
    )
Run Code Online (Sandbox Code Playgroud)

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

接下来,我们需要创建方法,java.lang.AutoCloseable它将创建一个(或覆盖现有的)using并编写一个Try.它使用一个tryUsingAutoCloseable封装的a tryUsingAutoCloseable,然后封装一个tryPrintToFile.和提升性能,默认缓冲区大小比默认为大得多的File定义,List[String]以及分配的值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))
              )
          }
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的FileWriter方法很有用,因为它需要BufferedWriter输入并将其发送到PrintWriter.现在让我们创建一个BufferedWriter方法,它将a defaultBufferSize写入a tryPrintToFile.

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

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))
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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
              }
          }
      }
    )
Run Code Online (Sandbox Code Playgroud)

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

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
      )
  )
Run Code Online (Sandbox Code Playgroud)

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


现在,将所有内容与提取的导入结合起来(使得更容易粘贴到Eclipse ScalaIDE和IntelliJ Scala插件中的Scala Worksheet中,以便轻松地将输出转储到桌面以便使用文本编辑器更轻松地进行检查),这就是代码的样子(简洁程度更高):

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
    )
  }
Run Code Online (Sandbox Code Playgroud)

作为一个Scala/FP的新手,我已经烧了好几个小时(主要是头疼的挫折),获得了上述知识和解决方案.我希望这有助于其他Scala/FP新手更快地克服这个特殊的学习驼峰.

  • 令人难以置信的更新。唯一的问题是现在你有大约 100 行代码可以用 `try-catch-finally` 替换。仍然爱你的热情。 (2认同)

Li *_*oyi 6

不幸的是,对于最佳答案,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")
Run Code Online (Sandbox Code Playgroud)

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