在Scala中读取整个文件?

时间:2009-08-16 14:33:21

标签: scala

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

我能想到的最好的是:

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

或者我应该使用Java's god-awful idioms中的一个,其中最好的(不使用外部库)似乎是:

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

19 个答案:

答案 0 :(得分:413)

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"

答案 1 :(得分:55)

为了扩展Daniel的解决方案,您可以通过将以下导入插入任何需要文件操作的文件来极大地缩短范围:

import scala.io.Source._

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

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

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

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

答案 2 :(得分:36)

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

答案 3 :(得分:26)

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

使用trunk:

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

答案 4 :(得分:17)

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

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

控制字符编码,无需清理资源。此外,可能已经优化(例如Files.readAllBytes分配适合文件大小的字节数组。)

答案 5 :(得分:7)

我被告知Source.fromFile存在问题。就个人而言,我在使用Source.fromFile打开大文件时遇到了问题,并且不得不求助于Java InputStreams。

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

答案 6 :(得分:6)

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

答案 7 :(得分:5)

就像在Java中一样,使用CommonsIO库:

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

此外,许多答案都忘了Charset。最好总是明确地提供它,否则它会在一天内提供。

答案 8 :(得分:4)

还有一个: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")

答案 9 :(得分:4)

为了模拟打开和读取文件的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

答案 10 :(得分:3)

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

import scalax.file.Path

现在您可以使用以下方法获取文件路径: -

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

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

答案 11 :(得分:3)

为了更快地整体阅读/上传(大)文件,请考虑增加bufferSizeSource.DefaultBufSize设置为2048)的大小,例如,如下所示,

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

注意Source.scala。有关进一步的讨论,请参阅Scala fast text file read and upload to memory

答案 12 :(得分:3)

由于连接泄漏,最好提及scala.io.Source

在新的孵化器项目(即scala-io)合并之前,可能scalax和纯Java库如commons-io是最好的选择。

答案 13 :(得分:2)

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

使用底层Java IO实用程序将FileReaderInputStream转换为{{1}来进行隐式转换并不是一件好事。 }。我认为缺乏可伸缩性意味着不将它添加到标准API是正确的。

答案 14 :(得分:1)

打印每一行,比如使用Java BufferedReader读取每一行,并打印出来:

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

当量:

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

答案 15 :(得分:1)

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

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
}

答案 16 :(得分:1)

filter

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

答案 17 :(得分:1)

如果您不介意第三方依赖性,则应考虑使用我的OS-Lib library。这使得读取/写入文件和使用文件系统非常方便:

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

具有用于reading bytesreading chunksreading lines以及其他许多有用/常见操作的一线帮助程序

答案 18 :(得分:0)

你可以使用

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

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

Source.fromFile(fileName).iter.mkString