如何解析文本直至遇到独立一行的##标记
Scala RegexParsers: Stop Parsing at Standalone
## Line 我来帮你搞定这个解析需求!你想要实现的是从BEGIN开始读取文本内容,一旦碰到单独一行的##就立即停止解析对吧?咱们可以基于Scala的RegexParsers来完成这个逻辑,下面是完整且可运行的实现方案:
完整解析器代码
import scala.util.parsing.combinator.RegexParsers object MyParser extends RegexParsers { // 匹配BEGIN关键字,同时跳过其后面的任意空白(包括同一行的空格) val begin: Parser[Unit] = "BEGIN" ~> """\s*""".r ^^^ () // 匹配非结束行的内容:确保整行不是单独的"##",然后捕获整行文本 val line: Parser[String] = """^(?!##$).+$""".r <~ eol // 匹配各种换行符格式 val eol: Parser[String] = "\r\n" | "\n" | "\r" // 匹配结束标记:单独一行的"##",允许后面没有换行符(比如文本最后一行就是##) val end: Parser[Unit] = """^##$""".r <~ opt(eol) ^^^ () // 主解析规则:先匹配BEGIN,然后重复解析所有有效行直到遇到结束标记 val document: Parser[Seq[String]] = begin ~> rep(line) <~ end // 对外暴露的解析方法,返回Either类型便于处理成功/失败场景 def parse(input: String): Either[String, Seq[String]] = { parseAll(document, input) match { case Success(result, _) => Right(result) case Failure(msg, pos) => Left(s"解析失败: $msg (位置: ${pos.line}, ${pos.column})") case Error(msg, pos) => Left(s"解析错误: $msg (位置: ${pos.line}, ${pos.column})") } } } // 测试用例 object ParserDemo extends App { val testDoc = """BEGIN A Bunch Of Text With linebreaks Some other content line Another line to parse ## This line won't be parsed at all Neither will this one""" MyParser.parse(testDoc) match { case Right(lines) => println("解析成功,结果如下:") lines.zipWithIndex.foreach { case (line, idx) => println(s"${idx+1}. $line") } case Left(error) => println(error) } }
关键逻辑说明
- 行匹配规则:
line解析器用了正则负向前瞻^(?!##$).+$,确保只匹配那些不是单独##的行,避免把结束标记行混入结果。 - 结束标记处理:
end解析器专门匹配单独一行的##,opt(eol)处理了文本最后一行就是##的边界情况(没有后续换行符)。 - 解析流程:主规则
document先跳过BEGIN关键字和后续空白,然后不断解析有效行,直到遇到end标记就停止,完美符合你的需求。 - 错误处理:对外暴露的
parse方法返回Either类型,方便你优雅处理解析成功或失败的场景。
运行测试用例后,你会得到前3行的内容,##后面的文本不会被解析,完全符合预期。
内容的提问来源于stack exchange,提问作者ThaDon
相关产品推荐
相关产品推荐

