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?
6 Answers 6
Answering my own question I'll use the "pimp my library pattern"
object RegexUtils {
implicit class RichRegex(val underlying: Regex) extends AnyVal {
def matches(s: String) = underlying.pattern.matcher(s).matches
}
}
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.
-
1I pimped with a function
misses
too. Match and missmatch :-) It's so annoying to have to write!s.matches(r)
instead ofs misses r
. HmmKajMagnus– KajMagnus2012年04月05日 12:38:59 +00:00Commented Apr 5, 2012 at 12:38 -
2How about the built-in
"5" matches "\\d"
which @polygenelubricants suggested?Erik Kaplun– Erik Kaplun2014年02月16日 21:17:39 +00:00Commented Feb 16, 2014 at 21:17 -
2Data matches a pattern, not vice-versa. The scaladoc on Regex makes a big deal about the lack of a boolean for "matches". Personally, I think you've swapped a nice match for a clunkier if-else. If you don't care about groups, use
case r(_*) =>
.som-snytt– som-snytt2014年05月14日 07:02:21 +00:00Commented May 14, 2014 at 7:02 -
1There has to be a way to do this without importing an external library...Jameela Huq– Jameela Huq2017年12月22日 22:38:21 +00:00Commented Dec 22, 2017 at 22:38
-
2@JameelaHuq Folks visiting this question will be pleased with 2.13, where Regex finally gets matches. github.com/scala/scala/pull/6521som-snytt– som-snytt2018年05月25日 14:13:30 +00:00Commented May 25, 2018 at 14:13
I don't know Scala all that well, but it looks like you can just do:
"5".matches("\\d")
References
-
26Well, 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– mkneissl2010年06月11日 10:57:33 +00:00Commented Jun 11, 2010 at 10:57
-
3@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– polygenelubricants2010年06月11日 11:18:35 +00:00Commented Jun 11, 2010 at 11:18 -
4Thanks, 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– mkneissl2010年06月11日 14:04:06 +00:00Commented Jun 11, 2010 at 14:04
-
2@ed. that's even slower and cruftier, so why?Erik Kaplun– Erik Kaplun2014年02月16日 21:20:23 +00:00Commented Feb 16, 2014 at 21:20
-
2The link given as a reference is brokenValy Dia– Valy Dia2019年04月12日 22:50:08 +00:00Commented Apr 12, 2019 at 22:50
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
-
5While true, the primary use of unapply and unapplySeq is implicitly in the
case
s of amatch
block.Randall Schulz– Randall Schulz2010年06月11日 15:06:43 +00:00Commented Jun 11, 2010 at 15:06
"""\d""".r.unapplySeq("5").isDefined //> res1: Boolean = true
"""\d""".r.unapplySeq("a").isDefined //> res2: Boolean = false
-
Hmm. Why posting a duplicate of stackoverflow.com/a/3022478/158823 two years later?mkneissl– mkneissl2013年01月16日 19:26:50 +00:00Commented Jan 16, 2013 at 19:26
-
3Your original question asked for a result ending in 'true' or 'false', not 'Some' or 'None'. As far as I'm aware isDefined was not part of the library 2 years ago, but maybe it was. Anyway, my answer is not a duplicate ;-)Jack– Jack2013年01月16日 19:38:36 +00:00Commented Jan 16, 2013 at 19:38
-
1I see, it isn't a duplicate. Sorry.mkneissl– mkneissl2013年01月16日 20:41:50 +00:00Commented Jan 16, 2013 at 20:41
-
2No probs ;-) My mistake, I should have explained why I'm using isDefined in my answer. Just giving code as an answer is generally a bad idea, so it's my bad.Jack– Jack2013年01月16日 20:52:15 +00:00Commented Jan 16, 2013 at 20:52
Using Standard Scala library and a pre-compiled regex pattern and pattern matching (which is scala state of the art):
val digit = """(\d)""".r
"2" match {
case digit( a) => println(a + " is Digit")
case _ => println("it is something else")
}
more to read: http://www.scala-lang.org/api/2.12.1/scala/util/matching/index.html
The answer is in the regex:
val Digit = """^\d$""".r
Then use the one of the existing methods.
-
3I 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– polygenelubricants2010年06月12日 17:18:04 +00:00Commented Jun 12, 2010 at 17:18 -
@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 C. Sobral– Daniel C. Sobral2010年06月14日 13:32:11 +00:00Commented Jun 14, 2010 at 13:32 -
Anchoring is not the problem. See also the "123" example in Vasil's answer.mkneissl– mkneissl2010年06月14日 18:15:26 +00:00Commented Jun 14, 2010 at 18:15
-
5@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– mkneissl2010年06月15日 07:45:29 +00:00Commented Jun 15, 2010 at 7:45
-
1@mkneissl I dislike the concept of Java's
matches
, but ok. As forOption
vsBoolean
, addnonEmpty
to the end and you'll get theBoolean
.Daniel C. Sobral– Daniel C. Sobral2010年06月15日 12:33:26 +00:00Commented Jun 15, 2010 at 12:33
"5" match { case Digit() => true case _ => false }
looks better than using underlying pattern object.