在Scala中读取整个文件?

Read entire file in Scala?

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

我能想到的最好办法是:

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

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

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

从阅读邮件列表讨论中,我不清楚scala.i o.source甚至应该是规范的I/O库。我不明白它的目的到底是什么。

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

1
2
3
Ruby    open("file.txt").read
Ruby    File.read("file.txt")
Python  open("file.txt").read()


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

顺便说一下,"scala."不是真正必要的,因为它总是在范围内,当然,您可以完全或部分导入IO的内容,并避免预先准备"IO"。

但是,上述操作会使文件保持打开状态。为了避免出现问题,您应该这样关闭它:

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

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

1
2
source.getLines mkString"
"


为了扩展Daniel的解决方案,您可以将以下导入插入任何需要文件操作的文件中,从而极大地缩短时间:

1
import scala.io.Source._

有了这个,您现在可以:

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

我会小心地把整个文件读到一个单独的String中。这是一个非常坏的习惯,它会比你想象的更快更难咬你。getLines方法返回Iterator[String]类型的值。它实际上是文件中的一个懒惰的光标,允许您只检查所需的数据,而不冒内存过剩的风险。

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


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


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

使用后备箱:

1
2
3
4
5
6
scala> io.File("/etc/passwd").slurp
res0: String =
##
# User Database
#
... etc


1
2
3
4
import java.nio.charset.StandardCharsets._
import java.nio.file.{Files, Paths}

new String(Files.readAllBytes(Paths.get("file.txt")), UTF_8)

控制字符编码,没有要清理的资源。此外,还可能进行了优化(例如,Files.readAllBytes分配适合文件大小的字节数组)。


我听说source.fromfile有问题。就个人而言,我在用Source.fromFile打开大文件时遇到问题,不得不求助于Java输入流。

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


在scala.io.source上使用getlines()将丢弃用于行终止符的字符(、

等)。

下面应该为字符保留IT字符,并且不执行过多的字符串连接(性能问题):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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一样,使用公共图书馆:

1
FileUtils.readFileToString(file, StandardCharsets.UTF_8)

此外,这里的许多答案都忘记了字符集。最好总是明确地提供它,否则总有一天会遇到麻烦。


还有一个:https://github.com/pathikrit/better files流和编解码器

在不将内容加载到内存中的情况下,可以使用多种方法来slurp文件:

1
2
3
4
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):

1
2
3
4
5
6
7
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")


为了模拟打开和读取文件的Ruby语法(并传递语义),考虑这个隐式类(scala 2.10和更高版本)。

1
2
3
4
5
6
7
8
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("
"
)
}

这样的话,

1
open("file.txt").read


正如少数人提到的,由于连接泄漏,最好避免使用scala.io.source。

在新的孵化器项目(即ScalaIO)被合并之前,Scalax和JavaJavaLIBs可能是最好的选择。


您还可以使用scala io中的路径来读取和处理文件。

1
import scalax.file.Path

现在,您可以使用以下命令获取文件路径:

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

您还可以包括终止符,但默认情况下它设置为false。


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

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

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


显而易见的问题是"为什么要读取整个文件?"如果文件变得非常大,这显然不是一个可扩展的解决方案。scala.io.SourcegetLines方法返回了Iterator[String]方法,非常有用和简洁。

使用下面的Java IO实用程序来生成一个隐式转换并将EDCOX1的4个EDE、EDCOX1的5个或EDCOX1的6个ED转换成EDCOX1的7个,这不是一个很大的工作。我认为缺乏可伸缩性意味着它们不将此添加到标准API中是正确的。


如果您不介意第三方依赖,您应该考虑使用我的OS lib库。这使得读/写文件和使用文件系统非常方便:

1
2
3
4
5
6
7
8
9
10
11
12
// 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")

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


您不需要解析每一行,然后再次连接它们…

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

我喜欢用这个:

1
2
3
4
5
6
7
8
9
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
}


1
2
3
4
5
6
7
8
import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}

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


打印每一行,就像使用JavaBuffeRead Read Erry线,并打印:

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

等效:

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