11

我想知道是否有可能从下面的语法中匹配正则表达式生成 MatchData。

object DateParser extends JavaTokenParsers {

    ....

    val dateLiteral = """(\d{4}[-/])?(\d\d[-/])?(\d\d)""".r ^^ {
        ... get MatchData
    }
}

当然,一种选择是在块内再次执行匹配,但是由于 RegexParser 已经执行了匹配,我希望它将 MatchData 传递给块,还是存储它?

4

4 回答 4

20

这是将您Regex转换为 a的隐式定义Parser

  /** A parser that matches a regex string */
  implicit def regex(r: Regex): Parser[String] = new Parser[String] {
    def apply(in: Input) = {
      val source = in.source
      val offset = in.offset
      val start = handleWhiteSpace(source, offset)
      (r findPrefixMatchOf (source.subSequence(start, source.length))) match {
        case Some(matched) =>
          Success(source.subSequence(start, start + matched.end).toString, 
                  in.drop(start + matched.end - offset))
        case None =>
          Failure("string matching regex `"+r+"' expected but `"+in.first+"' found", in.drop(start - offset))
      }
    }
  }

只需调整它:

object X extends RegexParsers {
  /** A parser that matches a regex string and returns the Match */
  def regexMatch(r: Regex): Parser[Regex.Match] = new Parser[Regex.Match] {
    def apply(in: Input) = {
      val source = in.source
      val offset = in.offset
      val start = handleWhiteSpace(source, offset)
      (r findPrefixMatchOf (source.subSequence(start, source.length))) match {
        case Some(matched) =>
          Success(matched,
                  in.drop(start + matched.end - offset))
        case None =>
          Failure("string matching regex `"+r+"' expected but `"+in.first+"' found", in.drop(start - offset))
      }
    }
  }
  val t = regexMatch("""(\d\d)/(\d\d)/(\d\d\d\d)""".r) ^^ { case m => (m.group(1), m.group(2), m.group(3)) }
}

例子:

scala> X.parseAll(X.t, "23/03/1971")
res8: X.ParseResult[(String, String, String)] = [1.11] parsed: (23,03,1971)
于 2009-11-29T17:06:14.760 回答
3

不,你不能这样做。如果您查看将正则表达式转换为 Parser 时使用的 Parser 的定义,它会丢弃所有上下文并仅返回完整匹配的字符串:

http://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_7_7_final/src/library/scala/util/parsing/combinator/RegexParsers.scala?view=markup#L55

不过,您还有其他几个选择:

  • 将您的解析器分解为几个较小的解析器(对于您实际要提取的标记)
  • 定义一个自定义解析器,用于提取所需的值并返回域对象而不是字符串

第一个看起来像

val separator = "-" | "/"
  val year = ("""\d{4}"""r) <~ separator
  val month = ("""\d\d"""r) <~ separator
  val day = """\d\d"""r

  val date = ((year?) ~ (month?) ~ day) map {
    case year ~ month ~ day =>
      (year.getOrElse("2009"), month.getOrElse("11"), day)
  }

意思是“需要这<~两个令牌在一起,但只给我第一个的结果。

意思是“需要这~两个标记在一起,并将它们绑定在一个模式匹配的 ~ 对象中。

?意味着解析器是可选的并且将返回一个选项。

.getOrElse位为解析器未定义值时提供默认值。

于 2009-11-29T16:01:59.843 回答
1

当 RegexParsers 实例中使用 Regex 时,RegexParsers 中的隐式 def regex(Regex): Parser[String]用于将该 Regex 应用于输入。在当前输入上成功应用 RE 时产生的 Match 实例用于在 regex() 方法中构造 Success,但仅​​使用其“结束”值,因此在该方法时任何捕获的子匹配都将被丢弃返回。

就目前而言(在我查看的 2.7 源代码中),我相信你不走运。

于 2009-11-29T15:48:14.057 回答
0

我使用 scala 2.8.1 遇到了类似的问题,并尝试使用RegexParsers该类解析“name:value”形式的输入:

package scalucene.query

import scala.util.matching.Regex
import scala.util.parsing.combinator._

object QueryParser extends RegexParsers {
  override def skipWhitespace = false

  private def quoted = regex(new Regex("\"[^\"]+"))
  private def colon = regex(new Regex(":"))
  private def word = regex(new Regex("\\w+"))
  private def fielded = (regex(new Regex("[^:]+")) <~ colon) ~ word
  private def term = (fielded | word | quoted)

  def parseItem(str: String) = parse(term, str)
}

看起来你可以像这样解析后获取匹配的组:

QueryParser.parseItem("nameExample:valueExample") match {
  case QueryParser.Success(result:scala.util.parsing.combinator.Parsers$$tilde, _) => {
      println("Name: " + result.productElement(0) + " value: " + result.productElement(1))
  }
}
于 2011-03-06T06:32:19.570 回答