This question already has an answer here:
Working regex fails when using Scala pattern matching
(1 answer)
Closed 5 years ago.
I have written the following code in scala:
val regex_str = "([a-z]+)(\\d+)".r
"_abc123" match {
case regex_str(a, n) => "found"
case _ => "other"
}
which returns "other", but if I take off the leading underscore:
val regex_str = "([a-z]+)(\\d+)".r
"abc123" match {
case regex_str(a, n) => "found"
case _ => "other"
}
I get "found". How can I find any ([a-z]+)(\\d+) instead of just at the beginning? I am used to other regex languages where you use a ^ to specify beginning of the string, and the absence of that just gets all matches.
Scala regex patterns default as "anchored", i.e. bound to beginning and end of target string.
You'll get the expected match with this.
val regex_str = "([a-z]+)(\\d+)".r.unanchored
Hi May be you need something like this,
val regex_str = "[^>]([a-z]+)(\\d+)".r
"_abc123" match {
case regex_str(a, n) => println(s"found $a $n")
case _ => println("other")
}
This will avoid the first character from your string.
Hope this helps!
The unapplySeq of the Regex tries to capture the whole input by default (treats the pattern as if it was between ^ and $).
There are two ways to capture inside the input:
use .* before and after the captures: val regex_str = ".*([a-z]+)(\\d+).*".r
do the same with .unanchored: val regex_str = "([a-z]+)(\\d+)".r.unanchored
Otherwise scala treats regular expression anchors the same way as in other languages; this one is an exception made for semantic reasons.
The regex extractor in scala pattern-matching attempts to match the entire string. If you want to skip some junk-characters in the beginning and in the end, prepend a . with a reluctant quantifier to the regex:
val regex_str = ".*?([a-z]+)(\\d+).*".r
val result = "_!+<>__abc123_%$" match {
case regex_str(a, n) => s"found a = '$a', n = '$n'"
case _ => "no match"
}
println(result)
This outputs:
found a = 'abc', n = '123'
Otherwise, don't use the pattern match with the extractor, use "...".r.findAllIn to find all matches.
Related
In below regex I need "test" as output but it gives complete string which matches the regex. How can I capture string between two groups?
val pattern = """\{outer.*\}""".r
println(pattern.findAllIn(s"try {outer.test}").matchData.map(step => step.group(0)).toList.mkString)
Input : "try {outer.test}"
expected Output : test
current output : {outer.test}
You may capture that part using:
val pattern = """\{outer\.([^{}]*)\}""".r.unanchored
val s = "try {outer.test}"
val result = s match {
case pattern(i) => i
case _ => ""
}
println(result)
The pattern matches
\{outer\. - a literal {outer. substring
([^{}]*) - Capturing group 1: zero or more (*) chars other than { and } (see [^{}] negated character class)
\} - a } char.
NOTE: if your regex must match the whole string, remove the .unanchored I added to also allow partial matches inside a string.
See the Scala demo online.
Or, you may change the pattern so that the first part is no longer as consuming pattern (it matches a string of fixed length, so it is possible):
val pattern = """(?<=\{outer\.)[^{}]*""".r
val s = "try {outer.test}"
println(pattern.findFirstIn(s).getOrElse(""))
// => test
See this Scala demo.
Here, (?<=\{outer\.), a positive lookbehind, matches {outer. but does not put it into the match value.
I want to extract part of a String that match one of the tow regex patterns i defined:
//should match R0010, R0100,R0300 etc
val rPat="[R]{1}[0-9]{4}".r
// should match P.25.01.21 , P.27.03.25 etc
val pPat="[P]{1}[.]{1}[0-9]{2}[.]{1}[0-9]{2}[.]{1}[0-9]{2}".r
When I now define my method to extract the elements as:
val matcher= (s:String) => s match {case pPat(el)=> println(el) // print the P.25.01.25
case rPat(el)=>println(el) // print R0100
case _ => println("no match")}
And test it eg with:
val pSt=" P.25.01.21 - Hello whats going on?"
matcher(pSt)//prints "no match" but should print P.25.01.21
val rSt= "R0010 test test 3,870"
matcher(rSt) //prints also "no match" but should print R0010
//check if regex is wrong
val pHead="P.25.01.21"
pHead.matches(pPat.toString)//returns true
val rHead="R0010"
rHead.matches(rPat.toString)//return true
I'm not sure if the regex expression are wrong but the matches method works on the elements. So what is wrong with the approach?
When you use pattern matching with strings, you need to bear in mind that:
The .r pattern you pass will need to match the whole string, else, no match will be returned (the solution is to make the pattern .r.unanchored)
Once you make it unanchored, watch out for unwanted matches: R[0-9]{4} will match R1234 in CSR123456 (solutions are different depending on what your real requirements are, usually word boundaries \b are enough, or negative lookarounds can be used)
Inside a match block, the regex matching function requires a capturing group to be present if you want to get some value back (you defined it as el in pPat(el) and rPat(el).
So, I suggest the following solution:
val rPat="""\b(R\d{4})\b""".r.unanchored
val pPat="""\b(P\.\d{2}\.\d{2}\.\d{2})\b""".r.unanchored
val matcher= (s:String) => s match {case pPat(el)=> println(el) // print the P.25.01.25
case rPat(el)=>println(el) // print R0100
case _ => println("no match")
}
Then,
val pSt=" P.25.01.21 - Hello whats going on?"
matcher(pSt) // => P.25.01.21
val pSt2_bad=" CP.2334565.01124.212 - Hello whats going on?"
matcher(pSt2_bad) // => no match
val rSt= "R0010 test test 3,870"
matcher(rSt) // => R0010
val rSt2_bad = "CSR00105 test test 3,870"
matcher(rSt2_bad) // => no match
Some notes on the patterns:
\b - a leading word boundary
(R\d{4}) - a capturing group matching exactly 4 digits
\b - a trailing word boundary
Due to the triple quotes used to define the string literal, there is no need to escape the backslashes.
Introduce groups in your patterns:
val rPat=".*([R]{1}[0-9]{4}).*".r
val pPat=".*([P]{1}[.]{1}[0-9]{2}[.]{1}[0-9]{2}[.]{1}[0-9]{2}).*".r
...
scala> matcher(pSt)
P.25.01.21
scala> matcher(rSt)
R0010
If code is written in the following way, the desired outcome will be generated. Reference API documentation followed is http://www.scala-lang.org/api/2.12.1/scala/util/matching/Regex.html
//should match R0010, R0100,R0300 etc
val rPat="[R]{1}[0-9]{4}".r
// should match P.25.01.21 , P.27.03.25 etc
val pPat="[P]{1}[.]{1}[0-9]{2}[.]{1}[0-9]{2}[.]{1}[0-9]{2}".r
def main(args: Array[String]) {
val pSt=" P.25.01.21 - Hello whats going on?"
val pPatMatches = pPat.findAllIn(pSt);
pPatMatches.foreach(println)
val rSt= "R0010 test test 3,870"
val rPatMatches = rPat.findAllIn(rSt);
rPatMatches.foreach(println)
}
Please, let me know if that works for you.
I have this verbose code that does shortcircuit Regex extraction / matching in Scala. This attempts to match a string with the first Regex, if that doesn't match, it attempts to match the string with the second Regex.
val regex1 : scala.util.matching.Regex = "^a(b)".r
val regex2 : scala.util.matching.Regex = "^c(d)".r
val s = ?
val extractedGroup1 : Option[String] = s match { case regex1(v) => Some(v) case _ => None }
val extractedGroup2 : Option[String] = s match { case regex2(v) => Some(v) case _ => None}
val extractedValue = extractedGroup1.orElse(extractedGroup2)
Here are the results:
s == "ab" then extractedValue == "b"
s == "cd" then extractedValue == "c"
s == "gg" then extractedValue == None.
My question is how can we combine the two regex into a single regex with the regex or operator, and still use Scala extractors. I tried this, but it's always the None case.
val regex : scala.util.matching.Regex = "^a(b)$ | ^c(d)$".r
val extractedValue: s match { case regex(v) => Some(v) case _ => None }
Don't use quality of life spaces within the regex, although they feel very scala-esque, they might be taken literary and your program expects that there should be a whitespace after the endOfString or space before the startOfString, which is obviously never the case. Try ^(?:a(b)|c(d))$, which is the same thing you did without repeating ^ and $.
Your own ^a(b)$|^c(d)$ can work too (if you remove the whitespaces).
Also, do you really get c out of cd? Judging by your regex, you should be getting d, if we're talking about capture groups.
Also, note that you're extracting capture groups. If you combine the regexes, an extracted d will be $2, while b would be $1.
val AlphabetPattern = "^([a-z]+)".r
def stringMatch(s: String) = s match {
case AlphabetPattern() => println("found")
case _ => println("not found")
}
If I try,
stringMatch("hello")
I get "not found", but I expected to get "found".
My understanding of the regex,
[a-z] = in the range of 'a' to 'z'
+ = one more of the previous pattern
^ = starts with
So regex AlphabetPattern is "all strings that start with one or more alphabets in the range a-z"
Surely I am missing something, want to know what.
Replace case AlphabetPattern() with case AlphabetPattern(_) and it works. The extractor pattern takes a variable to which it binds the result. Here we discard it but you could use x or whatever.
edit: Further to Randall's comment below, if you check the docs for Regex you'll see that it has an unapplySeq rather than an unapply method, which means it takes multiple variables. If you have the wrong number, it won't match, rather like
list match { case List(a,b,c) => a + b + c }
won't match if list doesn't have exactly 3 elements.
There are some issues with the match statement. s match is matching on the value of s which is checked against AlphabetPattern and _ which always evaluates to _ since s is never equal to "^([a-z]+)".r. Use one of the find methods in Scala.Util.Regex to look for a match with the given `Regex.
For example, using findFirstIn to find the first match of a string in AlphabetPattern.
scala> AlphabetPattern.findFirstIn("hello")
res0: Option[String] = Some(hello)
The stringMatch method using findFirstIn and a case statement:
scala> def stringMatch(s: String) = AlphabetPattern findFirstIn s match {
| case Some(s) => println("Found: " + s)
| case None => println("Not found")
| }
stringMatch: (s:String)Unit
scala> stringMatch("hello")
Found: hello
Let's say I have this code:
val string = "one493two483three"
val pattern = """two(\d+)three""".r
pattern.findAllIn(string).foreach(println)
I expected findAllIn to only return 483, but instead, it returned two483three. I know I could use unapply to extract only that part, but I'd have to have a pattern for the entire string, something like:
val pattern = """one.*two(\d+)three""".r
val pattern(aMatch) = string
println(aMatch) // prints 483
Is there another way of achieving this, without using the classes from java.util directly, and without using unapply?
Here's an example of how you can access group(1) of each match:
val string = "one493two483three"
val pattern = """two(\d+)three""".r
pattern.findAllIn(string).matchData foreach {
m => println(m.group(1))
}
This prints "483" (as seen on ideone.com).
The lookaround option
Depending on the complexity of the pattern, you can also use lookarounds to only match the portion you want. It'll look something like this:
val string = "one493two483three"
val pattern = """(?<=two)\d+(?=three)""".r
pattern.findAllIn(string).foreach(println)
The above also prints "483" (as seen on ideone.com).
References
regular-expressions.info/Lookarounds
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
}
Starting Scala 2.13, as an alternative to regex solutions, it's also possible to pattern match a String by unapplying a string interpolator:
"one493two483three" match { case s"${x}two${y}three" => y }
// String = "483"
Or even:
val s"${x}two${y}three" = "one493two483three"
// x: String = one493
// y: String = 483
If you expect non matching input, you can add a default pattern guard:
"one493deux483three" match {
case s"${x}two${y}three" => y
case _ => "no match"
}
// String = "no match"
You want to look at group(1), you're currently looking at group(0), which is "the entire matched string".
See this regex tutorial.
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")