我有一个包含对象文本表示的文件。我编写了一个组合器解析器语法,它解析文本并返回对象。在文本中,“#”是注释分隔符:忽略从该字符到行尾的所有内容。空行也被忽略。我想一次处理一行文本,这样我就可以处理非常大的文件。
我不想用通用注释和空行逻辑来混淆我的解析器语法。我想删除这些作为预处理步骤。通过第I行将文件转换为迭代器可以执行以下操作:
Source.fromFile("file.txt").getLines.map(_.replaceAll("#.*", "").trim).filter(!_.isEmpty)
如何将类似表达式的输出传递给组合子解析器?我无法弄清楚如何使用这样的过滤表达式创建Reader
对象。 Java FileReader
接口无法正常工作。
有没有办法做到这一点,还是应该将我的评论和空行逻辑放在解析器语法中?如果是后者,是否有一些util.parsing
包已经为我做了这个?
答案 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))
...将删除空行并反转非空行。