ChatGPT解决这个技术问题 Extra ChatGPT

在 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()
如果您知道正确的工具,Java 并不是那么糟糕。导入 org.apache.commons.io.FileUtils; FileUtils.readFileToString(new File("file.txt", "UTF-8")
此评论忽略了语言设计的重点。因此,任何具有用于您想要执行的操作的简单库函数的语言都与其函数调用语法一样好。给定一个无限且 100% 记忆的库,所有程序都将通过一个函数调用来实现。当一种编程语言需要较少的预制组件来实现特定结果时,它是很好的。
恐怕“给定一个无限且 100% 记忆的图书馆”不是任何理性论证的前提!编程语言是为人类设计的,理想情况下应该只包含将事物粘合在一起所需的抽象
最好的现代解决方案是使用 Li 的 os-lib as he mentioned here。 os-lib 隐藏了 Java 的丑陋并提供了 Ruby-like elegance

a
automorphic
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"

我参加聚会太晚了,但我讨厌人们不知道他们可以在后备箱中执行“io.File(”/etc/passwd“).slurp”。
@extempore 如果你真的认为我吃力不讨好,我真的很抱歉。我非常感谢您对 Scala 语言的支持,以及每次您亲自调查我提出的问题、为我遇到的问题提出解决方案或向我解释某些事情时。那么,我将借此机会感谢您将 scala.io 变成了体面和有价值的东西。从现在开始,我会更加直言不讳地表示感谢,但我仍然讨厌这个名字,对不起。
多年来,“slurp”一直是在 Perl 中一次读取整个文件的名称。 Perl 的命名传统比 C 语言家族更发自内心和非正式的命名传统,有些人可能会觉得这很反感,但在这种情况下,我认为它很合适:它是丑陋实践的丑陋词。当你 slurp() 时,你知道你在做一些顽皮的事情,因为你只需要输入它。
File.read() 将是一个更好的名称,并且与 Ruby 和 Python 保持一致。
@extempore:你无法阻止人们厌恶。就是这样。有些人不喜欢你所做的每一个选择,这不应该打扰你。这就是生活,你不能取悦所有人:)
D
Daniel Spiewak

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

import scala.io.Source._

有了这个,您现在可以执行以下操作:

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

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

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


好的。我对 Source 的负面印象有一个故事:我曾经处于与现在不同的情况,当时我有一个非常大的文件,无法放入内存。使用 Source 导致程序崩溃;事实证明,它正试图一次阅读整个内容。
Source 不应该将整个文件读入内存。如果您在 getLines 之后使用 toList 或其他会产生集合的方法,那么您会将所有内容都放入内存中。现在,Source 是一个 hack,旨在完成工作,而不是一个经过深思熟虑的库。它会在 Scala 2.8 中得到改进,但是 Scala 社区肯定有机会积极地定义一个好的 I/O API。
W
Walter Chang
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString

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

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"))更快。


为简化起见,您可以使用 Files.readString​(Path.of("file.txt"), StandardCharsets.UTF_8)
这个答案应该排名更高,因为它是实际关闭文件的答案中最短的答案。当然,它只使用 Java API,但在这种情况下没问题。 (没有暴露可变性)
@Ava,谢谢你的建议。我已经更新了最新的 Java 版本。
这是最好的答案:+1:
B
Brendan OConnor

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

使用中继:

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

slurp”?我们真的放弃了明显、直观的名称吗? slurp 的问题在于,至少对于以英语为第一语言的人来说,事后可能会有意义,但您一开始就不会想到它!
只是偶然发现了这个问题/答案。 File 不再在 2.8.0 中,不是吗?
啜饮听起来很棒。 :) 我没想到,但我也不希望屏幕上的输出被命名为“打印”。 slurp 太棒了! :) 太棒了?我没找到。 ;(
在 scala-2.10.0 中,包名称是 scala.reflect.io.File 关于这个“文件”的问题。临时,为什么这个文件被标记为“实验”?安全吗?它是否释放了对文件系统的锁定?
slurp 为此目的有着悠久的历史,我认为起源于 perl
I
Ikai Lan

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

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


M
Muyyatin

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

L
Li Haoyi

如果您不介意第三方依赖项,您应该考虑使用我的 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 和许多其他有用/常见操作提供单行帮助程序


你的图书馆很棒!感谢您继续支持他们。
D
Dzmitry Lazerka

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

FileUtils.readFileToString(file, StandardCharsets.UTF_8)

此外,这里的许多答案都忘记了 Charset。最好总是明确地提供它,否则它会在某一天发生。


p
pathikrit

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

在不将内容加载到内存的情况下 slurp 文件的各种方法:

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

e
elm

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

c
comonad

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

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
}

您应该关闭流 - 如果在 val content = source.mkString 中发生错误
Codec +1。我在 sbt test 上测试失败,因为无法设置它,而 Intellij 的测试命令通过了所有测试。您可以使用 this 中的 def using
o
oxbow_lakes

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

使用底层 Java IO 实用程序将 FileReaderInputStream 转换为 String 来进行隐式转换并不是一件容易的事。我认为缺乏可扩展性意味着他们不将其添加到标准 API 是正确的。


严重地?您真正定期阅读的文件中有多少在内存中存在实际问题?我处理过的绝大多数程序中的绝大多数文件都很容易小到可以放入内存中。坦率地说,大数据文件是个例外,如果你要读/写它们,你应该意识到这一点并相应地编程。
oxbow_lakes,我不同意。有很多情况涉及到将来大小不会增长的小文件。
我同意它们是例外——但我认为这就是为什么 JDK 或 Scala SDK 中都不存在 read-entire-file-into-memory 的原因。这是您自己编写的 3 行实用程序方法:克服它
p
poko

正如一些人提到的那样,由于连接泄漏,最好避免使用 scala.io.Source

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


A
Atiq

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

import scalax.file.Path

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

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

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


C
Community

为了更快地整体读取/上传(大)文件,请考虑增加 bufferSize 的大小(Source.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


g
gordonpro

打印每一行,比如使用 Java BufferedReader 读取每一行,然后打印:

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

相等的:

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

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

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


这提供了其他答案没有的什么?
还没有看到其他答案...只是想我可以在这里做出贡献所以发布...希望这不会伤害任何人:)
你真的应该阅读它们。大多数信息量很大。即使是8岁的也有相关信息。
Y
Y2Kot

您可以使用

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

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

Source.fromFile(fileName).iter.mkString

这个答案没有带来任何新的帮助,已经有很多答案和评论说同样的话。除非您可以为此添加更多上下文。请阅读:How to give a good answer?