用scala读取空文件

时间:2018-05-02 14:06:37

标签: scala readline

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

3 个答案:

答案 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 1Line 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

希望有所帮助。请询问您是否需要进一步澄清。