在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()
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"
只是为了扩展 Daniel 的解决方案,您可以通过将以下导入插入到任何需要文件操作的文件中来大大缩短时间:
import scala.io.Source._
有了这个,您现在可以执行以下操作:
val lines = fromFile("file.txt").getLines
我会警惕将整个文件读入单个 String
。这是一个非常坏的习惯,它会比你想象的更快更狠地咬你。 getLines
方法返回 Iterator[String]
类型的值。它实际上是文件中的一个惰性光标,允许您只检查所需的数据,而不会冒内存过剩的风险。
哦,回答您关于 Source
的隐含问题:是的,它是规范 I/O 库。大多数代码最终都使用 java.io
,因为它的接口较低,并且与现有框架的兼容性更好,但是任何可以选择的代码都应该使用 Source
,特别是对于简单的文件操作。
// for file with utf-8 encoding
val lines = scala.io.Source.fromFile("file.txt", "utf-8").getLines.mkString
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)
。
(编辑:这在 scala 2.9 中不起作用,也许在 2.8 中也不起作用)
使用中继:
scala> io.File("/etc/passwd").slurp
res0: String =
##
# User Database
#
... etc
slurp
”?我们真的放弃了明显、直观的名称吗? slurp
的问题在于,至少对于以英语为第一语言的人来说,事后可能会有意义,但您一开始就不会想到它!
File
不再在 2.8.0 中,不是吗?
slurp
太棒了! :) 太棒了?我没找到。 ;(
我被告知 Source.fromFile 有问题。就个人而言,我在使用 Source.fromFile 打开大文件时遇到了问题,不得不求助于 Java InputStreams。
另一个有趣的解决方案是使用 scalax。下面是一些注释良好的代码示例 使用 ManagedResource 打开日志文件以使用 scalax 帮助程序打开文件:http://pastie.org/pastes/420714
在 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)
}
如果您不介意第三方依赖项,您应该考虑使用我的 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 bytes、reading chunks、reading lines 和许多其他有用/常见操作提供单行帮助程序
就像在 Java 中一样,使用 CommonsIO 库:
FileUtils.readFileToString(file, StandardCharsets.UTF_8)
此外,这里的许多答案都忘记了 Charset。最好总是明确地提供它,否则它会在某一天发生。
还有一个: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")
为了模拟打开和读取文件的 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
您不需要解析每一行然后再次连接它们......
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
中发生错误
显而易见的问题是“你为什么要读取整个文件?”如果你的文件变得非常大,这显然不是一个可扩展的解决方案。 scala.io.Source
从 getLines
方法返回一个 Iterator[String]
,它非常有用且简洁。
使用底层 Java IO 实用程序将 File
、Reader
或 InputStream
转换为 String
来进行隐式转换并不是一件容易的事。我认为缺乏可扩展性意味着他们不将其添加到标准 API 是正确的。
正如一些人提到的那样,由于连接泄漏,最好避免使用 scala.io.Source。
在新的孵化器项目(即 scala-io)被合并之前,可能 scalax 和像 commons-io 这样的纯 java 库是最好的选择。
您还可以使用来自 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.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。
打印每一行,比如使用 Java BufferedReader 读取每一行,然后打印:
scala.io.Source.fromFile("test.txt" ).foreach{ print }
相等的:
scala.io.Source.fromFile("test.txt" ).foreach( x => print(x))
import scala.io.source
object ReadLine{
def main(args:Array[String]){
if (args.length>0){
for (line <- Source.fromLine(args(0)).getLine())
println(line)
}
}
在参数中,您可以提供文件路径,它将返回所有行
您可以使用
Source.fromFile(fileName).getLines().mkString
但是应该注意 getLines() 会删除所有换行符。如果你想保存格式,你应该使用
Source.fromFile(fileName).iter.mkString