我在scala中编写了以下函数,它将文件读入字符串列表。我的目的是确保如果文件输入为空,则返回的列表也是空的。知道如何以优雅的方式做到这一点:
def linesFromFile(file: String): List[String] = {
def materialize(buffer: BufferedReader): List[String] = materializeReverse(buffer, Nil).reverse
def materializeReverse(buffer: BufferedReader, accumulator: List[String]): List[String] = {
buffer.readLine match {
case null => accumulator
case line => materializeReverse(buffer, line :: accumulator)
}
}
val buffer = new BufferedReader(new FileReader(file))
materialize(buffer)
}
答案 0 :(得分:2)
您的代码应该可以运行,但内存使用效率相当低:您将整个文件读入内存,然后浪费更多内存并处理正确的行。
使用标准库中的Source.fromFile
方法是您最好的选择(也支持各种文件编码),如其他评论/答案中所述。
但是,如果您必须自己动手,我认为使用Stream
( lazy 形式的列表)比List
更有意义。您可以一次返回一行,并且可以在到达文件末尾时终止该流。这可以按如下方式完成:
import java.io.{BufferedReader, FileReader}
def linesFromFile(file: String): Stream[String] = {
// The value of buffer is available to the following helper function. No need to pass as
// an argument.
val buffer = new BufferedReader(new FileReader(file))
// Helper: retrieve next line from file. Called only when next value requested.
def materialize: Stream[String] = {
// Uncomment to demonstrate non-recursive nature of this method.
//println("Materialize called!")
// Read the next line and wrap in an option. This avoids the hated null.
Option(buffer.readLine) match {
// If we've seen the end of the file, return an empty stream. We're done reading.
case None => {
buffer.close()
Stream.empty
}
// Otherwise, prepend the line read to another call to this helper.
case Some(line) => line #:: materialize
}
}
// Start the process.
materialize
}
虽然看起来materialize
是递归的,但事实上它只在需要检索另一个值时调用,因此您不必担心堆栈溢出或递归。您可以通过取消注释println
来验证这一点。
例如(在 Scala REPL 会话中):
$ scala
Welcome to Scala 2.12.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala> import java.io.{BufferedReader, FileReader}
import java.io.{BufferedReader, FileReader}
scala> def linesFromFile(file: String): Stream[String] = {
|
| // The value of buffer is available to the following helper function. No need to pass as
| // an argument.
| val buffer = new BufferedReader(new FileReader(file))
|
| // Helper: retrieve next line from file. Called only when next value requested.
| def materialize: Stream[String] = {
|
| // Uncomment to demonstrate non-recursive nature of this method.
| println("Materialize called!")
|
| // Read the next line and wrap in an option. This avoids the hated null.
| Option(buffer.readLine) match {
|
| // If we've seen the end of the file, return an empty stream. We're done reading.
| case None => {
| buffer.close()
| Stream.empty
| }
|
| // Otherwise, prepend the line read to another call to this helper.
| case Some(line) => line #:: materialize
| }
| }
|
| // Start the process.
| materialize
| }
linesFromFile: (file: String)Stream[String]
scala> val stream = linesFromFile("TestFile.txt")
Materialize called!
stream: Stream[String] = Stream(Line 1, ?)
scala> stream.head
res0: String = Line 1
scala> stream.tail.head
Materialize called!
res1: String = Line 2
scala> stream.tail.head
res2: String = Line 2
scala> stream.foreach(println)
Line 1
Line 2
Materialize called!
Line 3
Materialize called!
Line 4
Materialize called!
注意当我们尝试从文件中读取另一行时,仅调用materialize
。此外,如果我们已经检索到一行,则不会调用它(例如,输出中的Line 1
和Line 2
在首次引用时仅以Materialize called!
开头。< / p>
关于空文件,在这种情况下,返回一个空流:
scala> val empty = linesFromFile("EmptyFile.txt")
Materialize called!
empty: Stream[String] = Stream()
scala> empty.isEmpty
res3: Boolean = true
答案 1 :(得分:0)
如果“空”意味着什么都没有,那么你的目标就已经实现了。
如果您的意思是“仅包含空格”,则可以通过修改materializeReverse
来过滤掉最终列表中仅包含空格的行。
def materializeReverse(buffer: BufferedReader, accumulator: List[String]): List[String] = {
buffer.readLine match {
case null => accumulator
case line if line.trim.isEmpty => materializeReverse(buffer, accumulator)
case line => materializeReverse(buffer, line :: accumulator)
}
}
答案 2 :(得分:0)
你可以试试这个方法
val result = Source.fromFile("C:\\Users\\1.txt").getLines.toList
希望有所帮助。请询问您是否需要进一步澄清。