在Scala中,将整个文件读入内存的简单而规范的方法是什么?(理想情况下,可以控制字符编码。)

我能想到的最好的是:

scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_)

或者我应该使用Java的一个可怕的习语,其中最好的(不使用外部库)似乎是:

import java.util.Scanner
import java.io.File
new Scanner(new File("file.txt")).useDelimiter("\\Z").next()

通过阅读邮件列表讨论,我甚至不清楚scala.io.Source是否应该是规范的I/O库。我不明白它的目的到底是什么。

... 我想要一些简单易记的东西。例如,在这些语言中,很难忘记成语……

Ruby    open("file.txt").read
Ruby    File.read("file.txt")
Python  open("file.txt").read()

val lines = scala.io.Source.fromFile("file.txt").mkString

顺便说一下,“scala.”并不是真正必要的,因为它总是在作用域中,当然,您可以完全或部分地导入io的内容,而不必在前面加上“io.”。

但是,上面的操作使文件保持打开状态。为了避免问题,你应该像这样关闭它:

val source = scala.io.Source.fromFile("file.txt")
val lines = try source.mkString finally source.close()

上面代码的另一个问题是,由于它的实现,它非常慢。对于较大的文件,应该使用:

source.getLines mkString "\n"

// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString

最明显的问题是“为什么要读取整个文件?”如果您的文件非常大,这显然不是一个可伸缩的解决方案。source从getLines方法返回一个Iterator[String],这非常有用和简洁。

使用底层java IO实用程序将File、Reader或InputStream转换为String并不是什么大工作。我认为缺乏可伸缩性意味着他们没有将其添加到标准API中是正确的。


为了扩展Daniel的解决方案,你可以通过在任何需要文件操作的文件中插入下面的导入来大大缩短时间:

import scala.io.Source._

有了这个,你现在可以做:

val lines = fromFile("file.txt").getLines

我会谨慎地将整个文件读入单个String。这是一个非常坏的习惯,它会比你想象的更快更严重地伤害你。getLines方法返回一个Iterator[String]类型的值。它实际上是文件中的惰性光标,允许您只检查所需的数据,而不会有内存过剩的风险。

哦,为了回答你隐含的关于Source的问题:是的,它是规范的I/O库。大多数代码最终都使用java。由于其较低级的接口和与现有框架的更好的兼容性,但是任何有选择的代码都应该使用Source,特别是对于简单的文件操作。


有人告诉我Source.fromFile有问题。就我个人而言,我在使用Source.fromFile打开大文件时遇到过问题,不得不求助于Java InputStreams。

另一个有趣的解决方案是使用scalax。下面是一些注释良好的代码示例,它使用ManagedResource打开日志文件,使用scalax helper打开文件:http://pastie.org/pastes/420714


(编辑:这在scala 2.9中不起作用,也许在2.8中也不起作用)

使用干:

scala> io.File("/etc/passwd").slurp
res0: String = 
##
# User Database
# 
... etc

正如一些人提到的,scala.io.Source最好避免使用,因为它会导致连接泄漏。

也许scalax和像commons-io这样的纯java库是最好的选择,直到新的孵化器项目(即scala-io)被合并。


在scala.io.Source上使用getLines()会丢弃用于行结束符的字符(\n, \r, \r\n等)。

下面应该保持字符对字符,并且不会进行过多的字符串连接(性能问题):

def fileToString(file: File, encoding: String) = {
  val inStream = new FileInputStream(file)
  val outStream = new ByteArrayOutputStream
  try {
    var reading = true
    while ( reading ) {
      inStream.read() match {
        case -1 => reading = false
        case c => outStream.write(c)
      }
    }
    outStream.flush()
  }
  finally {
    inStream.close()
  }
  new String(outStream.toByteArray(), encoding)
}

打印每一行,就像使用Java BufferedReader读取每一行,然后打印它:

scala.io.Source.fromFile("test.txt" ).foreach{  print  }

相当于:

scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))

你也可以使用scala io中的Path来读取和处理文件。

import scalax.file.Path

现在你可以得到文件路径使用这个:-

val filePath = Path("path_of_file_to_b_read", '/')
val lines = file.lines(includeTerminator = true)

您也可以包含终止符,但默认设置为false..


为了更快地读取/上传(大)文件,可以考虑增加bufferSize (Source. size)的大小。DefaultBufSize设置为2048),例如:

val file = new java.io.File("myFilename")
io.Source.fromFile(file, bufferSize = Source.DefaultBufSize * 2)

注意Source.scala。有关进一步讨论,请参阅Scala快速文本文件读取并上载到内存。


就像在Java中,使用CommonsIO库:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

此外,这里的许多答案忘记了Charset。最好总是显式地提供它,否则总有一天会出事。


为了模拟打开和读取文件的Ruby语法(以及传递语义),可以考虑以下隐式类(Scala 2.10及更高版本),

import java.io.File

def open(filename: String) = new File(filename)

implicit class RichFile(val file: File) extends AnyVal {
  def read = io.Source.fromFile(file).getLines.mkString("\n")
}

这样,

open("file.txt").read

还有一个:https://github.com/pathikrit/better-files#streams-and-codecs

有多种方法可以在不将内容加载到内存的情况下获取文件:

val bytes  : Iterator[Byte]            = file.bytes
val chars  : Iterator[Char]            = file.chars
val lines  : Iterator[String]          = file.lines
val source : scala.io.BufferedSource   = file.content 

你也可以为任何执行读/写操作的东西提供你自己的编解码器(如果你没有提供,它会假设scala.io.Codec.default):

val content: String = file.contentAsString  // default codec
// custom codec:
import scala.io.Codec
file.contentAsString(Codec.ISO8859)
//or
import scala.io.Codec.string2codec
file.write("hello world")(codec = "US-ASCII")

您不需要解析每一行,然后再次将它们连接起来。

Source.fromFile(path)(Codec.UTF8).mkString

我更喜欢用这个:

import scala.io.{BufferedSource, Codec, Source}
import scala.util.Try

def readFileUtf8(path: String): Try[String] = Try {
  val source: BufferedSource = Source.fromFile(path)(Codec.UTF8)
  val content = source.mkString
  source.close()
  content
}

Java 8 +

import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Paths}

val path = Paths.get("file.txt")
new String(Files.readAllBytes(path), StandardCharsets.UTF_8)

Java 11 +

import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}

val path = Path.of("file.txt")
Files.readString(path, StandardCharsets.UTF_8)

它们提供了对字符编码的控制,并且不需要清理资源。由于更高效的分配模式,它也比其他模式(例如getLines(). mkstring ("\n"))更快。


import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}

在参数中,你可以给出文件路径,它会返回所有行


如果您不介意第三方依赖,您应该考虑使用我的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")

使用单行帮助程序,用于读取字节、读取块、读取行和许多其他有用/常见操作


你可以使用

Source.fromFile(fileName).getLines().mkString

但是应该注意的是,getLines()删除了所有新的行字符。 如果你想保存格式,你应该使用

Source.fromFile(fileName).iter.mkString