tags:

views:

232

answers:

4

Assume I have a Regex pattern I want to match many Strings to.

val Digit = """\d""".r

I just want to check whether a given String fully matches the Regex. What is a good and idiomatic way to do this in Scala?

I know that I can pattern match on Regexes, but this is syntactically not very pleasing in this case, because I have no groups to extract:

scala> "5" match { case Digit() => true case _ => false }
res4: Boolean = true

Or I could fall back to the underlying Java pattern:

scala> Digit.pattern.matcher("5").matches
res6: Boolean = true

which is not elegant, either.

Is there a better solution?

+2  A: 

I don't know Scala all that well, but it looks like you can just do:

"5".matches("\\d")

References

polygenelubricants
Well, that works, but has the disadvantage that the pattern is compiled on every try to match. I'd like to avoid that for performance reasons.
mkneissl
@mkneissl: then it looks like your `.pattern.matcher(text).matches` is the way to go. You can hide the verbosity under some utility method or overloaded operator or something if Scala supports it.
polygenelubricants
@polygenelubricants: Thanks, that's what I am going to do, see my answer. I hope answering one's own questions is accepted behaviour on Stack Overflow... Meta says it is...
mkneissl
+5  A: 

For the full match you may use unapplySeq. This method tries to match target (whole match) and returns the matches.

scala> val Digit = """\d""".r
Digit: scala.util.matching.Regex = \d

scala> Digit unapplySeq "1"
res9: Option[List[String]] = Some(List())

scala> Digit unapplySeq "123"
res10: Option[List[String]] = None

scala> Digit unapplySeq "string"
res11: Option[List[String]] = None
Vasil Remeniuk
While true, the primary use of unapply and unapplySeq is implicitly in the `case`s of a `match` block.
Randall Schulz
+4  A: 

Answering my own question I'll use the "pimp my library pattern"

object RegexUtils {
  class RichRegex(underlying: Regex) {
    def matches(s: String) = underlying.pattern.matcher(s).matches
  }
  implicit def regexToRichRegex(r: Regex) = new RichRegex(r)
}

and use it like this

import RegexUtils._
val Digit = """\d""".r
if (Digit matches "5") println("match")
else println("no match")

unless someone comes up with a better (standard) solution.

Notes

  • I didn't pimp String to limit the scope of potential side effects.

  • unapplySeq does not read very well in that context.

mkneissl
+2  A: 

The answer is in the regex:

val Digit = """^\d$""".r

Then use the one of the existing methods.

Daniel
I don't think anchors is the issue here. `String/Pattern/Matcher.matches`, in Java at least, is whole string match already. I think the issue is just style/idiom for regex-ing in Scala, i.e. what those "one of the existing methods" are.
polygenelubricants
@polygenelubricants Well, `Matcher.matches` is an aberration. Ok, it makes some optimizations possible, though I don't know if the Java library actually takes advantage of it. But the _standard_ way for Regular Expressions to express that a full match is required is to use anchors. Since the Scala library does _not_ provide a full match method, then the proper way to do it is to use anchors. Either that, or use the Java library.
Daniel
Anchoring is not the problem. See also the "123" example in Vasil's answer.
mkneissl
@mkneissl In what way is `findFirstIn` with `"""^\d$""".r` different from using `unapplySeq` with `"""\d""".r`?
Daniel
@Daniel You might be missing the point -- My question was, if I only need to know if a regex matches fully, what is a good way to express that in Scala. There are a lot of working solutions, but in summary I think there is a method missing in Regex that just does that and nothing else.To answer the question in your commment: The difference from unapplySeq to findFirstMatch is, that I have to change the Regex to add the anchors. Both methods neither immediately express my intent nor return a boolean value, that is I'd have to go from Option to Boolean (no problem, but adding more clutter).
mkneissl
@mkneissl I dislike the concept of Java's `matches`, but ok. As for `Option` vs `Boolean`, add `nonEmpty` to the end and you'll get the `Boolean`.
Daniel