Scala regexps:如何将匹配作为数组或列表返回

时间:2010-01-14 17:35:04

标签: regex scala

有一种简单的方法可以将正则表达式匹配作为数组返回吗? 以下是我在2.7.7尝试的方式:

val s = """6 1 2"""
val re = """(\d+)\s(\d+)\s(\d+)""".r
for (m <- re.findAllIn (s)) println (m) // prints "6 1 2"
re.findAllIn (s).toList.length // 3? No! It returns 1!

但我接着尝试了:

s match {
  case re (m1, m2, m3) => println (m1)
}

这很好用! m1是6,m2是1,等等。

然后我发现了一些让我感到困惑的事情:

val mit = re.findAllIn (s)
println (mit.toString)
println (mit.length)
println (mit.toString)

打印:

non-empty iterator
1
empty iterator

“length”调用以某种方式修改迭代器的状态。这是怎么回事?

3 个答案:

答案 0 :(得分:27)

好的,首先,要了解findAllIn会返回IteratorIterator是一次消费可变对象。你做的任何事都会改变它。如果您不熟悉迭代器,请阅读它们。如果您希望它可重用,则将findAllIn的结果转换为List,并仅使用该列表。

现在,您似乎想要所有匹配的群组,而不是所有匹配。方法findAllIn将返回可在字符串中找到的完整正则表达式的所有匹配项。例如:

scala> val s = """6 1 2, 4 1 3"""
s: java.lang.String = 6 1 2, 4 1 3

scala> val re = """(\d+)\s(\d+)\s(\d+)""".r
re: scala.util.matching.Regex = (\d+)\s(\d+)\s(\d+)

scala> for(m <- re.findAllIn(s)) println(m)
6 1 2
4 1 3

看到有两个匹配,并且它们都不包含字符串中间的“,”,因为那不是任何匹配的一部分。

如果你想要这些小组,你可以这样得到它们:

scala> val s = """6 1 2"""
s: java.lang.String = 6 1 2

scala> re.findFirstMatchIn(s)
res4: Option[scala.util.matching.Regex.Match] = Some(6 1 2)

scala> res4.get.subgroups
res5: List[String] = List(6, 1, 2)

或者,使用findAllIn,如下所示:

scala> val s = """6 1 2"""
s: java.lang.String = 6 1 2

scala> for(m <- re.findAllIn(s).matchData; e <- m.subgroups) println(e)
6
1
2

matchData方法将Iterator返回Match而不是String

答案 1 :(得分:9)

unapplySeq如何解释多个组与findAllIn的工作方式之间存在差异。 findAllIn在字符串上扫描你的模式并返回匹配的每个字符串(如果成功则前进到匹配,或者如果失败则返回一个字符)。

所以,例如:

scala> val s = "gecko 6 1 2 3 4 5"
scala> re.findAllIn(s).toList
res3: List[String] = List(6 1 2, 3 4 5)

另一方面,unapplySeq假设完美与序列匹配。

scala> re.unapplySeq(s)
res4: Option[List[String]] = None

因此,如果要解析在精确正则表达式字符串中指定的组,请使用unapplySeq。如果要查找看起来像正则表达式模式的字符串子集,请使用findAllIn。如果你想两者兼顾,请自行链接:

scala> re.findAllIn(s).flatMap(text => re.unapplySeq(text).elements )
res5: List[List[String]] = List(List(6, 1, 2), List(3, 4, 5))

答案 2 :(得分:2)

试试这个:

  val s = """6 1 2"""
  val re = """\d+""".r
  println(re.findAllIn(s).toList) // List(6, 1, 2)
  println(re.findAllIn(s).toList.length) // 3

并且,如果你真的需要一个单一的Regex中的匹配组列表:

  val s = """6 1 2"""
  val Re = """(\d+)\s(\d+)\s(\d+)""".r
  s match {  // this is just sugar for calling Re.unapplySeq(s)
      case Re(mg@_*) => println(mg) // List(6, 1, 2)
  }