预处理Scala解析器Reader输入

时间:2012-11-12 20:40:11

标签: parsing scala combinators

我有一个包含对象文本表示的文件。我编写了一个组合器解析器语法,它解析文本并返回对象。在文本中,“#”是注释分隔符:忽略从该字符到行尾的所有内容。空行也被忽略。我想一次处理一行文本,这样我就可以处理非常大的文件。

我不想用通用注释和空行逻辑来混淆我的解析器语法。我想删除这些作为预处理步骤。通过第I行将文件转换为迭代器可以执行以下操作:

Source.fromFile("file.txt").getLines.map(_.replaceAll("#.*", "").trim).filter(!_.isEmpty)

如何将类似表达式的输出传递给组合子解析器?我无法弄清楚如何使用这样的过滤表达式创建Reader对象。 Java FileReader接口无法正常工作。

有没有办法做到这一点,还是应该将我的评论和空行逻辑放在解析器语法中?如果是后者,是否有一些util.parsing包已经为我做了这个?

2 个答案:

答案 0 :(得分:3)

最简单的方法是在fromLines上使用PagedSeq方法:

import scala.collection.immutable.PagedSeq
import scala.io.Source
import scala.util.parsing.input.PagedSeqReader

val lines = Source.fromFile("file.txt").getLines.map(
  _.replaceAll("#.*", "").trim
).filterNot(_.isEmpty)

val reader = new PagedSeqReader(PagedSeq.fromLines(lines))

现在您已经有scala.util.parsing.input.Reader可以插入解析器了。这基本上就是当您解析java.io.Reader时发生的事情 - 无论如何它会立即包裹在PagedSeqReader中。

答案 1 :(得分:0)

不是您编写过的最漂亮的代码,但您可以按照以下方式查看新的Source

val SEP = System.getProperty("line.separator")
def lineMap(fileName : String, trans : String=>String) : Source = {
  Source.fromIterable(
    Source.fromFile(fileName).getLines.flatMap(
      line => trans(line) + SEP
    ).toIterable
  )
}

说明:flatMap将在字符上生成一个迭代器,您可以将其转换为Iterable,您可以使用它来构建新的Source。您需要额外的SEP,因为getLines默认情况下将其删除(使用\n可能无效,因为Source无法正确分隔这些行。)

如果你也想要应用过滤,即删除一些行,你可以尝试:

// whenever `trans` returns `None`, the line is dropped.
def lineMapFilter(fileName : String, trans : String=>Option[String]) : Source = {
  Source.fromIterable(
    Source.fromFile(fileName).getLines.flatMap(
      line => trans(line).map(_ + SEP).getOrElse("")
    ).toIterable
  )
}

举个例子:

lineMapFilter("in.txt", line => if(line.isEmpty) None else Some(line.reverse))

...将删除空行并反转非空行。