Scala正则表达式多线匹配与负向前瞻

时间:2014-07-16 02:37:57

标签: regex scala regex-lookarounds

我正在使用Scala的解析器组合器编写DSL。我最近将我的基类从StandardTokenParsers更改为JavaTokenParsers,以利用我认为最后一部分需要的正则表达式功能。 (见Parsing a delimited multiline string using scala StandardTokenParser

我要做的是提取由某些字符(在此示例中为{{}})分隔的文本块。这个文本块可以跨越多行。到目前为止我所拥有的是:

  def docBlockRE = regex("""(?s)(?!}}).*""".r)
  def docBlock: Parser[DocString] =
      "{{" ~> docBlockRE <~ "}}" ^^ { case str => new DocString(str) }}

其中DocString是我的DSL中的案例类。但是,这不起作用。如果我喂它,它会失败:

{{
abc
}}

{{
abc
}}

我不确定为什么会失败。如果我放置一个Deubg包装器,在解析器周围有一个调试包装器(http://jim-mcbeath.blogspot.com/2011/07/debugging-scala-parser-combinators.html),我得到以下内容:

docBlock.apply for token 
   at position 10.2 offset 165 returns [19.1] failure: `}}' expected but end of source found

如果我尝试使用多行的单个分隔块:

{{
abc
def
}}

然后它也无法解析:

docBlock.apply for token 
  at position 10.2 offset 165 returns [16.1] failure: `}}' expected but end of source found

如果我删除了DOTALL指令(?s),那么我可以解析多个单行块(这对我来说并没有多大帮助)。

有没有办法将多行正则表达式与负前瞻相结合?

我对这种方法的另一个问题是,无论我做什么,结束分隔符必须与文本分开。否则我得到上面看到的同样的错误信息。这就像负面的前瞻并不像我期望的那样真正起作用。

2 个答案:

答案 0 :(得分:1)

要匹配{{the entire bracket}},请使用此正则表达式:

(?s)\{\{.*?\}\}

查看the demo中的匹配项。

要匹配{{inside the brackets}},请使用:

(?s)(?<=\{\{).*?(?=\}\})

查看the demo中的匹配项。

<强>解释

  • (?s)激活DOTALL模式,允许点跨行匹配
  • .*?中的星形量词是&#34;懒惰&#34;通过?,以便点只匹配必要的匹配。如果没有?.*将获取最长匹配,首先匹配整个字符串,然后仅根据需要进行回溯以允许下一个标记匹配。
  • (?<=\{\{)是一个断言,断言先于{{
  • (?=\}\})是一个先行者,断言后面的内容是}}

<强>参考

答案 1 :(得分:1)

在上下文中:

scala> val rr = """(?s).*?(?=}})""".r
rr: scala.util.matching.Regex = (?s).*?(?=}})

scala> object X extends JavaTokenParsers {val r: Parser[String] = rr; val b: Parser[String] = "{{" ~>r<~"}}" ^^ { case s => s } }
defined object X

scala> X.parseAll(X.b, """{{ abc
     | def
     | }}""")
res15: X.ParseResult[String] =
[3.3] parsed: abc
def

更多显示贪婪的差异:

scala> val rr = """(?s)(.*?)(?=}})""".r.unanchored
rr: scala.util.matching.UnanchoredRegex = (?s)(.*?)(?=}})

scala> def f(s: String) = s match { case rr(x) => x case _ => "(none)" }
f: (s: String)String

scala> f("something }} }}")
res3: String = "something "

scala> val rr = """(?s)(.*)(?=}})""".r.unanchored
rr: scala.util.matching.UnanchoredRegex = (?s)(.*)(?=}})

scala> def f(s: String) = s match { case rr(x) => x case _ => "(none)" }
f: (s: String)String

scala> f("something }} }}")
res4: String = "something }} "

前瞻只是意味着&#34;确保这跟着我,但不要消耗它。&#34;

否定前瞻只是意味着确保它不会跟随我。