如何在Scala中写入文件?

时间:2011-01-05 12:58:59

标签: scala file-io scala-2.8

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

17 个答案:

答案 0 :(得分: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() }
}

它的使用方式如下:

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

答案 1 :(得分:69)

编辑2019(8年后),Scala-IO不是非常活跃,Li Haoyi建议他自己的库lihaoyi/os-libpresents below


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

Scala-IO 已更改位置:从GitHub repo(另请Jesse Eichar)查看其on SO

  

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

     
      
  • 核心 - 核心主要处理从任意来源和接收器读取和写入数据。角石特征是InputOutputSeekable,它们提供核心API。
      其他重要类别包括ResourceReadCharsWriteChars
  •   
  • 文件 - 文件是File(称为Path)API,它基于Java 7 NIO文件系统和SBT PathFinder API的组合。
      PathFileSystem是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库 (如“Why doesn't Scala Source close the underlying InputStream?”中所述)

请参阅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)
  }

答案 2 :(得分: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() }

然后我用它作为:

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

答案 3 :(得分: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()
  }

答案 4 :(得分:20)

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

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

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

这类似于Jus12,但没有详细程度和正确的code style

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,并注意占位符语法的用法。另请注意更好的命名。

答案 5 :(得分: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 write string to file in one statement

答案 6 :(得分: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

答案 7 :(得分:6)

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

=IFERROR(INDEX($B$2:$B$12,MATCH(F$5&$E6,$A$2:$A$12&$C$2:$C$12,0)),"")

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

答案 8 :(得分:5)

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

  1. Jus12's answer中,对于Scala / FP初学者来说,使用curper进行使用辅助方法是不明显的
  2. 需要使用scala.util.Try
  3. 封装较低级别的错误
  4. 需要向Java开发人员展示Scala / FP的新手如何正确嵌套依赖资源,以便以相反的顺序对每个相关资源执行close方法 - 注意:< / strong>以相反的顺序关闭依赖资源特别是在失败的情况下java.lang.AutoCloseable规范中很少被理解的要求,这往往导致非常有害且难以发现的错误并且运行时间失败
  5. 在开始之前,我的目标并不简洁。这有助于更容易理解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方法的开头可能会令人困惑,因为它似乎有两个参数列表而不是惯用的单个参数列表。这称为currying。我不会详细说明currying的工作原理或偶尔有用的地方。事实证明,对于这个特定的问题空间,它是适合这项工作的正确工具。

    接下来,我们需要创建方法tryPrintToFile,它将创建(或覆盖现有的)File并编写List[String]。它使用由FileWriter封装的BufferedWriterPrintWriter又封装BufferedWriter。为了提升性能,定义了一个远大于defaultBufferSize默认值的默认缓冲区大小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)) ) } } } } ,并为其指定值65536。

    这是代码(再次,简洁不是这里的目标):

    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上使用Source方法来释放底层JVM和文件系统句柄。如果不这样做,那么在JVM GC(垃圾收集器)绕过释放finalize实例本身之前,资源不会被释放。即便如此,只有一个弱JVM保证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
          )
      )
    

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

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

    已提供updated version of the above function作为different but related StackOverflow question的答案。

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

    public void shiftArray(int[][] array, int shift) {
    
        for (int row = 0; row < array.length; row++) {
            int rowLength = array[row].length;
    
            // keep shift within bounds of the array
            shift = shift % rowLength;
    
            // copy out elements that will "fall off"
            int[] tmp = new int[shift];
            for (int i = 0; i < shift; i++) {
                tmp[i] = array[row][i];
            }
    
            // shift like normal
            for (int col = 0; col < rowLength - shift; col++) {
                array[row][col] = array[row][col + shift];
            }
    
            // copy back the "fallen off" elements
            for (int i = 0; i < shift; i++) {
                array[row][i + (rowLength - shift)] = tmp[i];
            }
        }
    }
    

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

答案 9 :(得分: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

答案 10 :(得分: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))

答案 11 :(得分: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")
}

答案 12 :(得分:1)

如果你的项目中有Akka Streams,它提供了一个单行:

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

Akka docs&gt; Streaming File IO

答案 13 :(得分: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. 关闭流

答案 14 :(得分:1)

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

SELECT 
    [LNMAJORTP], [MainType],
    SUM(x + y - z) [x]
FROM 
    [my_table]  
WHERE 
    state LIKE '%firstCondition%'
GROUP BY
    [LNMAJORTP], [MainType] 

SELECT 
    [LNMAJORTP], [MainType],
    SUM(x + y - z) [y]
FROM 
    [my_table]  
WHERE 
    state LIKE '%secondCondition%'
GROUP BY
    [LNMAJORTP], [MainType] 

SELECT 
    [LNMAJORTP], [MainType],
    SUM(x + y - z) [z]
FROM 
    [my_table]  
WHERE
    state LIKE '%thirdCondition%'
GROUP BY
    [LNMAJORTP], [MainType]

它具有writing to filesappending to filesoverwriting files以及其他许多有用/常见操作的单行代码

答案 15 :(得分:1)

类似于this answer,下面是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)

  }
}

答案 16 :(得分:0)

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

在这种情况下,它可以与诸如PrintWriterBufferedWriter之类的资源一起使用,这些资源扩展了AutoCloseable以便写入文件,无论如何,之后都应关闭该资源:

  • 例如,使用java.io api:

    import scala.util.Using
    import java.io.{PrintWriter, File}
    
    // val lines = List("hello", "world")
    Using(new PrintWriter(new File("file.txt"))) {
      writer => lines.foreach(writer.println)
    }
    // scala.util.Try[Unit] = Success(())
    
  • 或使用java.nio api:

    import scala.util.Using
    import 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"))
    }
    // scala.util.Try[Unit] = Success(())