Scala regex pattern extractor

Viewed 78

What am I doing wrong here:

val pattern = "([1-7]),([0-1][0-9]|[2][0-3])([0-1][0-9]|[2][0-3])".r
val pattern(count, fruit) = "7,2323"

gives:

[info]   Scenario: For a set of combination of hour formats, map them to the expected 24 hours format1 *** FAILED ***
[info]   scala.MatchError: 7,2323 (of class java.lang.String)
[info]   at com.mf.location.os.service.util.HoursFormatUtil$.fromHours(HoursFormatUtil.scala:55)
[info]   at com.mf.location.os.service.util.HoursFormatUtilSpec.$anonfun$new$9(HoursFormatUtilSpec.scala:71)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]   at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]   at org.scalatest.featurespec.AnyFeatureSpecLike$$anon$1.apply(AnyFeatureSpecLike.scala:257)
[info]   at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
[info]   at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)

However println(s"matches ${pattern.matches("7,2323")}") returns true.

2 Answers

If your count value should be 7 and the fruit value should be 2323, you can use

val pattern = "([1-7]),((?:[0-1][0-9]|2[0-3]){2})".r
val line = "7,2323"
val (count, fruit) = line match {
  case pattern(count, fruit) => (count,fruit)
  case _ => ("","")
}
println(s"${count} and ${fruit}")
// => 7 and 2323

See the Scala demo.

Note the ([1-7]),((?:[0-1][0-9]|2[0-3]){2}) refactored pattern that now contains only two capturing groups. pattern(count, fruit) "extracts" the capturing group values and assigns them to count and fruit. If there is no match, these variable will be empty strings.

Just to add on top of the great answer by @Wiktor, please note that in your regex, you expect 3 capturing groups, while you are trying to assign them into 2 variables. This is the reason you get a matching error.

"([1-7]),([0-1][0-9]|[2][0-3])([0-1][0-9]|[2][0-3])".r
 ↑     ↑ ↑                    ↑↑                   ↑
 group 1      group 2                 group 3

If for example you try to do:

val pattern = "([1-7]),([0-1][0-9]|[2][0-3])([0-1][0-9]|[2][0-3])".r
val pattern(count, fruit, other) = "7,2323"
println(s"count: $count fruit: $fruit other: $other")

The output is:

count: 7 fruit: 23 other: 23

However, when you later on do pattern.matches("7,2323") you get true, because there is a match. You just don't capture it by the correct groups amount.

Code run at Scastie.

Related