使用正则表达式在Scala中捕获分组

89

假设我有以下代码:

val string = "one493two483three"
val pattern = """two(\d+)three""".r
pattern.findAllIn(string).foreach(println)

我原本期望findAllIn只返回483,但实际却返回了two483three。我知道可以使用unapply来提取想要的部分,但需要针对整个字符串设置一个模式,类似于:

 val pattern = """one.*two(\d+)three""".r
 val pattern(aMatch) = string
 println(aMatch) // prints 483

还有其他的方法可以实现这一点,不直接使用 Java.util 中的类,也不使用 unapply 吗?
5个回答

120

以下是一个示例,展示如何访问每个匹配的 group(1):

val string = "one493two483three"
val pattern = """two(\d+)three""".r
pattern.findAllIn(string).matchData foreach {
   m => println(m.group(1))
}

这会打印出"483"在ideone.com上看到)。


lookaround选项

根据模式的复杂程度,您还可以使用lookaround来匹配所需的部分。它将类似于此:

val string = "one493two483three"
val pattern = """(?<=two)\d+(?=three)""".r
pattern.findAllIn(string).foreach(println)

上面的代码还打印出"483" (在ideone.com上查看)。

参考资料


2
你也可以使用 pattern.findAllMatchIn(string).foreach... 来代替。 - ruhong

52
val string = "one493two483three"
val pattern = """.*two(\d+)three.*""".r

string match {
  case pattern(a483) => println(a483) //matched group(1) assigned to variable a483
  case _ => // no match
}

7
这是最简单的方法。你在match/case中使用正则表达式对象("pattern"),将组提取到变量a483中。 这种情况的问题在于,模式应该在两侧都有通配符: val pattern = """.two(\d+)three.""".r - makingthematrix
是的。我认为上面的内容并不立即清晰,但一旦你理解它是将数字匹配组分配给变量'a483',那么它就更有意义了。也许可以以更清晰的方式重写? - Brian Agnew
1
这是使用正则表达式的Scala方式。对于那些不理解这个答案背后的魔力的人,可以尝试搜索“scala regex extractor”或“scala unapply regex”等内容。 - JasonWayne
语义不清。这是字符串的第一个、最后一个还是随机匹配? - user239558

22

Scala 2.13 开始,除了使用正则表达式解决方案,还可以通过解构字符串插值器String 进行模式匹配:

"one493two483three" match { case s"${x}two${y}three" => y }
// String = "483"

甚至可以这样:

val s"${x}two${y}three" = "one493two483three"
// x: String = one493
// y: String = 483

如果你期望非匹配的输入,你可以添加一个默认的模式保护:

"one493deux483three" match {
  case s"${x}two${y}three" => y
  case _                   => "no match"
}
// String = "no match"

16
您希望查看 group(1),您当前正在查看的是group(0),它代表的是"整个匹配字符串"。
请参考这篇正则表达式教程

1
你能否根据我提供的输入进行说明?我尝试在findAllIn返回的内容上调用group(1),但是出现了IllegalStateException异常。 - Geo

5
def extractFileNameFromHttpFilePathExpression(expr: String) = {
//define regex
val regex = "http4.*\\/(\\w+.(xlsx|xls|zip))$".r
// findFirstMatchIn/findAllMatchIn returns Option[Match] and Match has methods to access capture groups.
regex.findFirstMatchIn(expr) match {
  case Some(i) => i.group(1)
  case None => "regex_error"
}
}
extractFileNameFromHttpFilePathExpression(
    "http4://testing.bbmkl.com/document/sth1234.zip")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接