在Scala中读取整个文件?

Bre*_*nor 302 scala

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

我能想到的最好的是:

scala.io.Source.fromPath("file.txt").getLines.reduceLeft(_+_)
Run Code Online (Sandbox Code Playgroud)

或者我应该使用Java的一个令人难以置信的习语,其中最好的(不使用外部库)似乎是:

import java.util.Scanner
import java.io.File
new Scanner(new File("file.txt")).useDelimiter("\\Z").next()
Run Code Online (Sandbox Code Playgroud)

从阅读邮件列表讨论来看,我不清楚scala.io.Source甚至应该是规范的I/O库.我完全不明白它的目的是什么.

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

Ruby    open("file.txt").read
Ruby    File.read("file.txt")
Python  open("file.txt").read()
Run Code Online (Sandbox Code Playgroud)

Dan*_*ral 420

val lines = scala.io.Source.fromFile("file.txt").mkString
Run Code Online (Sandbox Code Playgroud)

顺便说一句," scala."并不是必需的,因为它总是在范围内,当然,您可以完全或部分地导入io的内容,并且避免必须添加"io".太.

但是,上面的文件保持打开状态.为避免出现问题,您应该像这样关闭它:

val source = scala.io.Source.fromFile("file.txt")
val lines = try source.mkString finally source.close()
Run Code Online (Sandbox Code Playgroud)

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

source.getLines mkString "\n"
Run Code Online (Sandbox Code Playgroud)

  • "slurp"一直是在Perl中读取整个文件多年的名称.Perl比C语言系列具有更为内在和非正式的命名传统,有些人可能会觉得这些语言令人反感,但在这种情况下我觉得它很合适:对于丑陋的练习来说,这是一个丑陋的词.当你啜饮()时,你知道你正在做一些顽皮的事情,因为你只需输入它. (48认同)
  • 我已经来晚了,但是我讨厌那些不知道他们可以做"io.File("/ etc/passwd")的人."在行李箱里啜饮. (45认同)
  • @extempore如果你真的觉得我没办法,我真的很抱歉.我非常感谢您对Scala语言的支持,并且每次您亲自调查我提出的问题时,建议解决我遇到的问题,或者向我解释一些问题.那么,我将借此机会感谢您将scala.io转变为体面和值得的东西.从现在开始,我会更加直言不讳,但我仍然讨厌这个名字,对不起. (27认同)
  • @extempore:你无法阻止人们反感.这就是它的方式.有些人不喜欢你所做的每一个选择都不应该打扰你.这只是生活,你不能取悦每个人:) (26认同)
  • File.read()将是一个更好的名称,并与Ruby和Python一致. (15认同)
  • 请注意,只需调用fromFile.getLines即可实例化Source实例但不关闭它.这意味着Scala运行时可能会保留对文件系统中文件的锁定,从而防止它被打开以进行写入,重命名,删除等,只要Scala Source insance持有锁定即可.以下将读取文件并关闭文件.def linesFrom(fileName:String):List [String] = {val source = scala.io.Source.fromFile(fileName)val text = source.getLines source.close()text} (13认同)
  • 我讨厌Scala 2.8有一个名为"`slurp`"的方法,但似乎我仍然坚持使用它. (6认同)
  • 我不想要更多的声音谢谢,我希望你和其他所有人能够压抑这种压倒性的厌恶,而只是提出建议.另外要记住,我是唯一一个在大多数标准库和大部分编译器上工作的人,而且只有这么多小时. (6认同)
  • 我会就这个名字进行谈判,但由于你"完全厌恶",我会尽力保持原样.谢谢你特别的吃力不讨好. (5认同)
  • 赞成使用`slurp` - 现在永远不会忘记它. (5认同)
  • 顺便说一句,裸的`mkString`(没有`getLines`)在2.11中得到修复. (2认同)

Dan*_*wak 56

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

import scala.io.Source._
Run Code Online (Sandbox Code Playgroud)

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

val lines = fromFile("file.txt").getLines
Run Code Online (Sandbox Code Playgroud)

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

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

  • 源不应该将整个文件读入内存.如果在getLines之后使用toList,或者使用其他一些会生成集合的方法,那么就可以将所有内容都放入内存中.现在,Source是一个*hack*,旨在完成工作,而不是一个经过深思熟虑的库.它将在Scala 2.8中得到改进,但Scala社区肯定有机会积极定义一个好的I/O API. (7认同)

Wal*_*ang 36

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

  • 另请参阅我在Daniel C. Sobral的回答中的评论 - 此用法不会关闭Source实例,因此Scala可能会保留对文件的锁定. (9认同)
  • 将"getLines"添加到原始答案将删除所有换行符.应该是"Source.fromFile("file.txt","utf-8").mkString". (6认同)

psp*_*psp 26

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

使用trunk:

scala> io.File("/etc/passwd").slurp
res0: String = 
##
# User Database
# 
... etc
Run Code Online (Sandbox Code Playgroud)

  • "`slurp`"?我们真的抛弃了明显,直观的名字吗?"slurp"的问题在于,事实上,对于至少有英语作为第一语言的人来说,它可能是有意义的,但你永远不会想到它! (14认同)
  • 只是偶然发现了这个问题/答案.`文件`不再是2.8.0,不是吗? (5认同)
  • 在scala-2.10.0中,包名是scala.reflect.io.File以及关于这个"文件"的问题.即席,为什么这个文件被标记为"实验性的"?安全吗?它是否释放了对文件系统的锁定? (5认同)
  • 啜食听起来很棒.:)我不指望它,但我没想到输出到屏幕上也被命名为'print'.`slurp`太棒了!:)太棒了?我找不到.( (4认同)
  • 你仍然可以从scala.tools.nsc.io.File偷偷进入它,虽然我认为将来可能会改变位置,所以使用风险自负.;-)哦,让我插话说我多么讨厌"啜饮"这个名字. (3认同)
  • 在Clojure中它也被称为slurp (3认同)
  • 对于这个目的,slurp有着悠久的历史,我认为,来自perl (3认同)

Pau*_*per 18

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

new String(Files.readAllBytes(Paths.get("file.txt")), UTF_8)
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案应该排名更高,因为它是实际“关闭文件”的最短答案。当然,它仅使用 Java API,但在本例中这很好。(没有暴露可变性) (2认同)
  • 这是最好的答案:+1: (2认同)

小智 7

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

另一个有趣的解决方案是使用scalax.这是一个很好评论的代码示例,它使用ManagedResource打开一个日志文件,用scalax助手打开一个文件:http://pastie.org/pastes/420714


小智 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)
}
Run Code Online (Sandbox Code Playgroud)


Dzm*_*rka 5

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

FileUtils.readFileToString(file, StandardCharsets.UTF_8)
Run Code Online (Sandbox Code Playgroud)

此外,许多答案在这里忘记了Charset.最好总是明确地提供它,否则它会在一天内发布.


pat*_*rit 5

还有一个: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 
Run Code Online (Sandbox Code Playgroud)

您也可以为进行读/写的任何操作提供自己的编解码器(如果不提供,则假定为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")
Run Code Online (Sandbox Code Playgroud)


Li *_*oyi 5

如果你不介意第三方依赖,你应该考虑使用我的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")
Run Code Online (Sandbox Code Playgroud)

具有用于读取字节读取块读取行和许多其他有用/常见操作的单行助手