如何在Scala中写入文件?


157

为了阅读,这里有有用的抽象Source。如何将行写入文本文件?


1
如果您知道如何用Java做到这一点,那么可以在Scala中使用相同的方法。您的问题专门针对Scala标准库吗?
小麦味2011年

1
@wheaties是在scala中做到这一点的最佳方法
yura


Answers:


71

编辑2019年(8年后),Scala-IO并不十分活跃,如果有的话,李浩一建议他自己的图书馆lihaoyi/os-lib,他在下面介绍

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


编辑(2011年9月):自从爱德华多·科斯塔询问Scala2.9,自里克- 777评论认为scalax.IO提交历史几乎是不存在的,因为2009年年中...

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

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

  • 核心 -核心主要处理与任意源和宿之间的数据读写。基石特质InputOutput以及Seekable其提供的核心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)

原始答案(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)
  }

15
那么Scala 2.9版本呢?:)
Eduardo Costa

scalax项目似乎已经死了(自2009年6月以来没有提交)。这是正确的吗?scalax commit history
Rick-

@Eduardo:我用新的地方scala-io库(已经为Scala2.9更新了它的答案:github.com/jesseeichar/scala-io/issues/20
VonC

10
这真的是针对Scala 2.10的当前建议吗?使用Scala IO?Scala核心中还没有任何内容吗?
菲尔(Phil)

2
我从未使用过scalax.io,但是从这些示例行来看,它的API设计似乎很糟糕。在一个接口中混合字符和二进制数据的方法几乎没有意义,并且很可能导致难以发现的编码错误。java.io的设计(阅读器/写入器与InputStream / OutputStream)似乎更好。
jcsahnwaldt恢复莫妮卡

211

这是标准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构造函数。
jcsahnwaldt恢复莫妮卡

@JonaChristopherSahnwaldt-当然,在特殊情况下,您可能需要指定编码。平台的默认值是平均而言最明智的默认值。与相同Source(默认情况下为默认编码)。当然enc: Option[String] = Nonef如果发现这是常见的需求,则可以在后面添加例如参数。
Rex Kerr

6
@RexKerr-我不同意。几乎在所有情况下都应指定编码。我遇到的大多数编码错误都是由于人们不了解或不考虑编码而发生的。他们使用默认值,甚至不知道它,因为太多的API让他们无法使用它。如今,最明智的默认设置可能是UTF-8。也许您只使用英语和其他可以ASCII编写的语言。幸运的你。我住在德国,不得不修理比我更想记住的更多的变音符号。
jcsahnwaldt恢复莫妮卡

3
@JonaChristopherSahnwaldt-这是使用明智的默认编码的原因,而不是强迫所有人一直指定它。但是,如果您使用的是Mac,并且Java编写的文件不是Mac OS Roman编码的,那么它们是用gobbledygook编写的,那么我不确定它的弊大于利。我认为他们没有就字符集达成共识是平台的错。作为一个单独的开发人员,输入字符串确实不能解决问题。(所有同意UTF-8的开发人员都可以,但是默认情况下可以使用。)
Rex Kerr 2013年

@JonaChristopherSahnwaldt +10,用于修复所有损坏的变音符号。不能使用锤子,也许不能打孔吗?还是他们已经需要填补漏洞,也许这个人可以帮助youtube.com/watch?v=E-eBBzWEpwE但是,严重的是,ASCII的影响在世界范围内是如此,同意应将其指定为默认值,并且默认为UTF- 8日
达沃斯

50

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

等等


39
别误会,我喜欢您的代码,它非常有教育意义,但是我看到这样的简单问题构造越多,就越让我想起古老的“ hello world”笑话:ariel.com.au/jokes/The_Evolution_of_a_Programmer .html :-)(我投了1票)。
greenoldman 2011年

4
如果您要编写一线工作,那么什么都没有。如果您正在编写重要的程序(大型程序需要不断维护和发展),则这种想法会导致最迅速,最有害的软件质量下降。
兰德尔·舒尔茨

3
在一定程度的练习之前,并不是每个人都会有“ scala eyes”的感觉-有趣的是,看到此代码示例来自“ Beginning” Scala
asyncwait 2012年

asyncwait“开始” scala ...有史以来最具有讽刺意味的标题,请注意:我已经读过这本书……而现在我才开始理解它。 ........
user1050817

1
问题不在于这里的Scala技巧,而是冗长和拙劣的风格。我对此进行了编辑,使其更具可读性。在我重构之后,它只有4行(好吧,其中4行具有IDE行长,此处使用6行以适合屏幕)。恕我直言,这是一个非常不错的答案。
samthebest

38

一个简单的答案:

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来自的库吗?
丹尼尔(Daniel)

1
从Java 7开始,改用java.nio.file:def writeToFile(file:String,stringToWrite:String):Unit = {val writer = Files.newBufferedWriter(Paths.get(file))最后尝试writer.write(stringToWrite) }
E Shindler's

20

提供另一个答案,因为我对其他答案的修改被拒绝了。

这是最简洁明了的答案(类似于Garret Hall的答案

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,也不需要lambda,并且请注意占位符语法的用法。还要注意更好的命名。


2
抱歉,您的代码是可以想象的,但不满足implemented前提条件。您不能使用未实现的代码。我的意思是您必须告诉它如何找到它,因为它默认情况下不可用并且不知名。
2015年

15

这是使用Scala编译器库的简洁明了的代码:

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

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

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

什么进口?即文件从哪里来?
Ben Hutchison

Scala编译器库。
2013年

3
不再可行(不在Scala 2.11中)
Brent Faust

1
你在说什么?scala.tools.nsc.io.File("/tmp/myFile.txt")在Scala 2.11.8中工作。

1
现在位于scala.reflect.io.File中
Keith Nordstrom

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


为什么这不适用于大文件?
Chetan Bhasin

2
@ChetanBhasin可能是因为write它将复制contents到一个新的字节数组,而不是将其流式传输到文件,从而在其峰值时消耗的内存是contents单独内存的两倍。
Daniel Werner

10

不幸的是,对于最佳答案,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")

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


谢谢您的更新。已投票。我在上面已经引用了您的答案,以提高知名度。
VonC

7

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

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

要么

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

同样,在这里-这个问题是在谷歌搜索如何使用Scala编写文件时的热门话题之一-既然您的项目越来越大,您可能想扩展一下答案?
asac

6

首先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)
    }
  • 或使用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"))
    }

6

2019年9月1日更新:

  • 从Scala 2.13开始,最好使用scala.util.Using
  • 修复了如果代码抛出错误finally会吞下原始文件的错误ExceptiontryfinallyException

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

  1. Jus12的答案中,对于Scala / FP初学者来说,使用辅助方法使用curring并不明显
  2. 需要封装较低级别的错误 scala.util.Try
  3. 需要向刚接触Scala / FP的Java开发人员展示如何正确嵌套依赖资源,以便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 => {
        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
              }
          }
      }
    )

上面tryUsingAutoCloseable方法的开头可能会令人困惑,因为它似乎有两个参数列表,而不是通常的单个参数列表。这称为“咖喱”。而且,我不会详细介绍curry的工作原理或在什么地方有用。事实证明,对于这个特定的问题空间,这是完成任务的正确工具。

接下来,我们需要创建方法,tryPrintToFile该方法将创建一个(或覆盖现有的)File并编写一个List[String]。它使用FileWriterBufferedWriter封装的,由封装的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方法很有用,因为它将a List[String]作为输入并将其发送到File。现在让我们创建一个tryWriteToFile方法,该方法采用a String并将其写入a 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))
      }
  }
}

最后,能够以a的File形式获取a的内容很有用String。虽然scala.io.Source提供了一种方便的方法来轻松获取a的内容File,但close必须在上使用该方法Source来释放基础JVM和文件系统句柄。如果不这样做,则直到JVM GC(垃圾收集器)释放Source实例本身之前,资源才被释放。即使这样,JVM仍然只能保证finalize将GC调用该方法到close资源。这意味着,显式调用该close方法是客户的责任,就像客户close对某个实例的责任是相同的。java.lang.AutoCloseable。为此,我们需要对handles的using方法的第二个定义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 => {
        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 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问题的解答


现在,将所有内容与提取的导入内容结合在一起(使粘贴到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
    )
  }

作为Scala / FP的新手,我已经花了很多时间(大部分时间都在抓挠自己的挫败感),获得了上述知识和解决方案。我希望这可以帮助其他Scala / FP新手更快克服这种特殊的学习障碍。


2
令人难以置信的更新。唯一的问题是,现在您有100行代码可以替换为try-catch-finally。仍然爱你的激情。
2016年

1
@Observer我会断言这是不正确的陈述。我正在描述的模式实际上是在减少客户端必须编写的样板数量,以确保正确处理AutoCloseables的关闭,同时还启用了使用scala.util.Try的Scala惯用FP模式。如果您尝试通过手动编写try / catch / finally块来达到与我相同的效果,那么我认为您最终会得到比您想象的要多得多的样板。因此,将所有样板都推到Scala函数的100行中具有很大的可读性。
chaotic3quilibrium

1
很抱歉,这听起来是否令人反感。我的观点仍然是,不需要如此大量的代码,因为通过非功能性方法可以更简单地实现相同的代码。就个人而言,我将最终尝试再加上一些额外的检查。它只是更短。如果我想使用包装器,则ApacheUtils可以使用所有脏工作。此外,所有标准的阅读器/写入器都会关闭基础流,因此不需要您的multipwrap。PS:为了支持您的努力,我的投票已从负一票改为一票正票。因此,请不要怀疑我的恶意。
观察家

没有冒犯。
chaotic3quilibrium 16/09/26

1
我了解您的观点。感谢您的讨论,我不得不考虑一下。祝你今天愉快!
2016年

3

这是使用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

为了超越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))

这使用依赖于反射的“鸭子类型”。在许多情况下,依赖反射是没有开始的。
chaotic3quilibrium

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

2019更新:

简介-Java NIO(或用于异步的NIO.2)仍然是Scala支持的最全面的文件处理解决方案。以下代码创建一些文本并将其写入新文件:

import java.io.{BufferedOutputStream, OutputStream}
import java.nio.file.{Files, Paths}

val testFile1 = Paths.get("yourNewFile.txt")
val s1 = "text to insert in file".getBytes()

val out1: OutputStream = new BufferedOutputStream(
  Files.newOutputStream(testFile1))

try {
  out1.write(s1, 0, s1.length)
} catch {
  case _ => println("Exception thrown during file writing")
} finally {
  out1.close()
}
  1. 导入Java库:IO和NIO
  2. Path使用您选择的文件名创建一个对象
  3. 将要插入文件的文本转换为字节数组
  4. 以流形式获取文件: OutputStream
  5. 将字节数组传递到输出流的write函数中
  6. 关闭流

1

此答案类似,fs2(版本1.0.4)的示例:

import cats.effect._

import fs2._
import fs2.io

import java.nio.file._

import scala.concurrent.ExecutionContext
import scala.language.higherKinds
import cats.syntax.functor._

object ScalaApp extends IOApp {

  def write[T[_]](p: Path, s: String)
                 (implicit F: ConcurrentEffect[T], cs: ContextShift[T]): T[Unit] = {
    Stream(s)
      .covary[T]
      .through(text.utf8Encode)
      .through(
        io.file.writeAll(
          p,
          scala.concurrent.ExecutionContext.global,
          Seq(StandardOpenOption.CREATE)
        )
      )
      .compile
      .drain
  }


  def run(args: List[String]): IO[ExitCode] = {

    implicit val executionContext: ExecutionContext =
      scala.concurrent.ExecutionContext.Implicits.global

    implicit val contextShift: ContextShift[IO] =
      IO.contextShift(executionContext)

    val outputFile: Path = Paths.get("output.txt")

    write[IO](outputFile, "Hello world\n").as(ExitCode.Success)

  }
}

0

这行代码有助于从数组或字符串中写入文件。

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

0

无论如何,如果您的项目中有Akka Streams,它会提供一线式:

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

Akka文档> 流式文件IO

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.