为了阅读,这里有有用的抽象Source
。如何将行写入文本文件?
为了阅读,这里有有用的抽象Source
。如何将行写入文本文件?
Answers:
编辑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有两个主要组件:
- 核心 -核心主要处理与任意源和宿之间的数据读写。基石特质
Input
,Output
以及Seekable
其提供的核心API。
其它重要的类Resource
,ReadChars
和WriteChars
。- 文件 -文件是一种
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)
}
这是标准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)
}
Source
(默认情况下为默认编码)。当然enc: Option[String] = None
,f
如果发现这是常见的需求,则可以在后面添加例如参数。
与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)
}
}
等等
一个简单的答案:
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()
}
import
来自的库吗?
提供另一个答案,因为我对其他答案的修改被拒绝了。
这是最简洁明了的答案(类似于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,并且请注意占位符语法的用法。还要注意更好的命名。
implemented
前提条件。您不能使用未实现的代码。我的意思是您必须告诉它如何找到它,因为它默认情况下不可用并且不知名。
这是使用Scala编译器库的简洁明了的代码:
scala.tools.nsc.io.File("filename").writeAll("hello world")
另外,如果您想使用Java库,可以执行以下操作:
Some(new PrintWriter("filename")).foreach{p => p.write("hello world"); p.close}
一个内衬保存/阅读/从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
write
它将复制contents
到一个新的字节数组,而不是将其流式传输到文件,从而在其峰值时消耗的内存是contents
单独内存的两倍。
不幸的是,对于最佳答案,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")
我写的一个微型图书馆:https : //github.com/pathikrit/better-files
file.appendLine("Hello", "World")
要么
file << "Hello" << "\n" << "World"
首先Scala 2.13
,标准库提供了专用的资源管理实用程序:Using
。
在这种情况下,它可以与诸如PrintWriter
或BufferedWriter
扩展AutoCloseable
以便写入文件的资源一起使用,无论如何,之后都应关闭资源:
例如,使用java.io
api:
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.nio
api:
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"))
}
2019年9月1日更新:
finally
会吞下原始文件的错误Exception
try
finally
Exception
在回顾了有关如何在Scala中轻松编写文件的所有这些答案之后,其中一些非常不错,我遇到了三个问题:
scala.util.Try
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]
。它使用FileWriter
由BufferedWriter
封装的,由封装的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新手更快克服这种特殊的学习障碍。
try-catch-finally
。仍然爱你的激情。
这是使用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
为了超越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))
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")
}
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()
}
Path
使用您选择的文件名创建一个对象OutputStream
write
函数中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)
}
}