Scala regex pattern match groups different from that using findAllIn - regex

I find that the groups extracted by Pattern-matching on regex's in Scala are different from those extracted using findAllIn function.
1) Here is an example of extraction using pattern match -
scala> val fullRegex = """(.+?)=(.+?)""".r
fullRegex: scala.util.matching.Regex = (.+?)=(.+?)
scala> val x = """a='b'"""
x: String = a='b'
scala> x match { case fullRegex(l,r) => println( l ); println(r) }
a
'b'
2) And here is an example of extraction using the findAllIn function -
scala> fullRegex.findAllIn(x).toArray
res4: Array[String] = Array(a=')
I was expecting the returned Array using findAllIn to be Array(a, 'b'). Why is it not so?

This is because you have not specified to what extent the second lazy match should go. So after = it consumes just one character and stops as it is in lazy mode.
See here.
https://regex101.com/r/dU7oN5/10
Change it to .+?=.+ to get full array

In particular, the pattern match's use of unapplySeq uses Matcher.matches, while findAllIn uses Matcher.find. matches tries to match entire input.
scala> import java.util.regex._
import java.util.regex._
scala> val p = Pattern compile ".+?"
p: java.util.regex.Pattern = .+?
scala> val m = p matcher "hello"
m: java.util.regex.Matcher = java.util.regex.Matcher[pattern=.+? region=0,5 lastmatch=]
scala> m.matches
res0: Boolean = true
scala> m.group
res1: String = hello
scala> m.reset
res2: java.util.regex.Matcher = java.util.regex.Matcher[pattern=.+? region=0,5 lastmatch=]
scala> m.find
res3: Boolean = true
scala> m.group
res4: String = h
scala>

Related

Regex pattern to detect payload.* pattern

I want to validate all strings that have 'payload.*' i.e the string must start with 'payload' followed by a period (.) and followed by minimum 1 character.
example :-
Input1 :- payload.Hello Output1 :-> Valid
Input2 :- Hipayload.Hello Output1 :-> InValid
Input3 :- payload.H Output1 :-> Valid
Input4 :- payload. Output1 :-> InValid
You can use x.matches(y) (where x and y are both Strings) to match against a Regex pattern in Scala (or use "raw" Strings):
scala> val regex: String = "^payload\\..+"
regex: String = ^payload\..+
scala> val regexAltRaw: String = raw"^payload\..+"
regexAltRaw : String = ^payload\..+
scala> val regexAltTripleQuotes: String = """^payload\..+"""
regexAltTripleQuotes : String = ^payload\..+
scala> "".matches(regex)
res0: Boolean = false
scala> "payload.Hello".matches(regex)
res1: Boolean = true
scala> "Hipayload.Hello".matches(regex)
res2: Boolean = false
scala> "payload.H".matches(regex)
res3: Boolean = true
scala> "payload.".matches(regex)
res4: Boolean = false
To explain the pattern:
^payload - starts with "payload"
\\. - "." literally (without using "raw" Strings, Scala requires you to use double back-slashes to escape rather than single slashes like you would in normal Regex)
.+ - any character, one or more times
In order to avoid validation of input payload.. you can use foolowing regex:
^payload\.[^.]+$
Input Array:
val ar = Array("payload.Hello","Hipayload.Hello","payload.H","payload.")
Regex String:
val p = """^payload\..{1,}"""
In Scala REPL:
scala> val ar = Array("payload.Hello","Hipayload.Hello","payload.H","payload.")
ar: Array[String] = Array(payload.Hello, Hipayload.Hello, payload.H, payload.)
scala> val p = """^payload\..{1,}"""
p: String = ^payload\..{1,}
Test:
scala> ar.map(x=>if(x.matches(p))"Valid" else "InValid")
res3: Array[String] = Array(Valid, InValid, Valid, InValid)

strange behaviour with filter?

I want to extract MIME-like headers (starting with [Cc]ontent- ) from a multiline string:
scala> val regex = "[Cc]ontent-".r
regex: scala.util.matching.Regex = [Cc]ontent-
scala> headerAndBody
res2: String =
"Content-Type:application/smil
Content-ID:0.smil
content-transfer-encoding:binary
<smil><head>
"
This fails
scala> headerAndBody.lines.filter(x => regex.pattern.matcher(x).matches).toList
res4: List[String] = List()
but the "related" cases work as expected:
scala> headerAndBody.lines.filter(x => regex.pattern.matcher("Content-").matches).toList
res5: List[String] = List(Content-Type:application/smil, Content-ID:0.smil, content-transfer-encoding:binary, <smil><head>)
and:
scala> headerAndBody.lines.filter(x => x.startsWith("Content-")).toList
res8: List[String] = List(Content-Type:application/smil, Content-ID:0.smil)
what am I doing wrong in
x => regex.pattern.matcher(x).matches
since it returns an empty List??
The reason for the failure with the first line is that you use the java.util.regex.Matcher.matches() method that requires a full string match.
To fix that, use the Matcher.find() method that searches for the match anywhere inside the input string and use the "^[Cc]ontent-" regex (note that the ^ symbol will force the match to appear at the start of the string).
Note that this line of code does not work as you expect:
headerAndBody.lines.filter(x => regex.pattern.matcher("Content-").matches).toList
You run the regex check against the pattern Content-, and it is always true (that is why you get all the lines in the result).
See this IDEONE demo:
val headerAndBody = "Content-Type:application/smil\nContent-ID:0.smil\ncontent-transfer-encoding:binary\n<smil><head>"
val regex = "^[Cc]ontent-".r
val s1 = headerAndBody.lines.filter(x => regex.pattern.matcher(x).find()).toList
println(s1)
val s2 = headerAndBody.lines.filter(x => regex.pattern.matcher("Content-").matches).toList
print (s2)
Results (the first is the fix, and the second shows that your second line of code fails):
List(Content-Type:application/smil, Content-ID:0.smil, content-transfer-encoding:binary)
List(Content-Type:application/smil, Content-ID:0.smil, content-transfer-encoding:binary, <smil><head>)
Your regexp should match all line but not only first sub-string.
val regex = "[Cc]ontent-.*".r

regular expression matching string in scala

I have a string like this
result: String = /home/administrator/com.supai.common-api-1.8.5-DEV- SNAPPSHOT/com/a/infra/UserAccountDetailsMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV- SNAPSHOT/com/a/infra/UserAccountDetailsMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserAccountMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserAccountMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserOverridenFunctionMetaDataMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserOverridenFunctionMetaDataMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserOverridenPermissionMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserOverridenPermissionMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserRoleMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/UserRoleMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV- SNAPSHOT/com/a/infra/VendorAddressMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/VendorAddressMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/reactore/infra/VendorContactMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/reactore/infra/VendorContactMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/reactore/infra/VendorMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/VendorMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/WeekMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/WeekMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/WorkflowMetadataMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/WorkflowMetadataMetaData.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/WorkflowNotificationMetaData$.class
/home/administrator/com.supai.common-api-1.8.5-DEV-SNAPSHOT/com/a/infra/WorkflowNotificationMetaData.class
/home/a/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/a/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
/home/common/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/raghav/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/sysadmin/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/tmp/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
regex: scala.util.matching.Regex = (\\/([u|s|r])\\/([s|h|a|r|e]))
x: scala.util.matching.Regex.MatchIterator = empty iterator`
and out of this how can I get only this part /usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jarand this part can be anywhere in the string, how can I achieve this, I tried using regular expression in Scala but don't know how to use forward slashes, so anybody plz explain how to do this in scala.
What is your search criteria? Your pattern seems to be wrong.
In your rexexp, I see u|s|r which means to search for either u, or s or r . See here for more information
how can I get only this part
/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jarand
this part can be anywhere in the string
If you are looking for a path, see the below example:
scala> val input = """/home/common/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
| /home/raghav/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
| /home/sysadmin/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
| /home/tmp/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
| /home/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
| /home/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
| /usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar"""
input: String =
/home/common/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/raghav/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/sysadmin/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/tmp/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/usr/share/common-api/lib/com.supai.common-api-1.3-SNAPSHOT.jar
/home/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
scala> val myRegExp = "/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar".r
myRegExp: scala.util.matching.Regex = /usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
scala> val myRegExp2 = "helloWorld.jar".r
myRegExp2: scala.util.matching.Regex = helloWorld.jar
scala> (myRegExp findAllIn input) foreach( println)
/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
/usr/share/common-api/lib/com.supai.common-api-1.8.5-DEV-SNAPSHOT.jar
scala> (myRegExp2 findAllIn input) foreach( println)
scala>

Scala: how to construct a regex including a variable?

I want to test if a regex including a variable that I defined before matches a string.
For instance I would like to do :
val value = "abc"
regex = "[^a-z]".r + value //something like that
if(regex matches ".abc") print("ok, it works")
So my question is : how can I add construct a regex including a variable in scala?
("[^a-z]" + value).r
is all you need
You must quote the value string to protect against special regex syntax.
scala> val value = "*"
value: String = *
scala> val oops = """[^a-z]""" + value r
oops: scala.util.matching.Regex = [^a-z]*
scala> ".*" match { case oops() => }
scala> ".....*" match { case oops() => }
They added quoting to the Scala API:
scala> import util.matching._
import util.matching._
scala> val ok = """[^a-z]""" + Regex.quote(value) r
ok: scala.util.matching.Regex = [^a-z]\Q*\E
scala> ".*" match { case ok() => }
scala> ".....*" match { case ok() => }
scala.MatchError: .....* (of class java.lang.String)
... 33 elided
You could also generalize the pattern and do additional checks:
scala> val r = """\W(\w+)""".r
r: scala.util.matching.Regex = \W(\w+)
scala> ".abc" match { case r(s) if s == "abc" => }
Parsing and building the regex itself is relatively expensive, so usually it's desirable to do it once with a general pattern.

Scala Replacement by Regex

I've a string like
val bar = "M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo".
Now I split this string and define the pattern
val split = bar.split("-")
val pattern = ".*(A|K)\\d.*".r
and now I want to replace A9K9foo in the last entry of 'split'
val last = split.last
val suffix = last match {
case pattern(_) => last replaceFirst ("""(A\d)?(K\d)?.*""", "")
case _ => last
}
What I know is that replaceFirst is executed but it won't replace A9K9foo in my 'last' val
(replaceFirst should only executed if 'last' matches 'pattern'), the wanted result is M2.
Edit: It could happen that last is not M9A9K9foo but M9A9 or M9K9foo or maybe M9A9K9. All i want is to replace all content except the text before A\d or K\d but if there is no A\d or K\d nothing should happen.
Do you know why this replacement won't work?
You're using String.replaceFirst, and your pattern has a wildcard that consumes everything.
Maybe you want:
last replaceFirst ("""A\dK\d""", "")
where the A9K9 is not optional, and that's all you want to replace.
There are other formulations:
scala> val r = """(A\dK\d)""".r
r: scala.util.matching.UnanchoredRegex = (A\dK\d)
scala> val m = (r findAllMatchIn bar).toList.last
m: scala.util.matching.Regex.Match = A9K9
scala> s"${m.before}${m.after}"
res15: String = M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9foo
That's not the most clever.
More:
scala> val r = """(A|K)\d""".r
r: scala.util.matching.Regex = (A|K)\d
scala> val bar = "M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo"
bar: String = M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo-M9A9K9foo
scala> val last = (bar split "-").last
last: String = M9A9K9foo
scala> r findFirstMatchIn last map (_.before) getOrElse last
res0: CharSequence = M9
scala> val r = """(.*?)((A|K)\d.*)?""".r
r: scala.util.matching.Regex = (.*?)((A|K)\d.*)?
scala> last match { case r(prefix, _*) => prefix }
res1: String = M9
scala> "M9" match { case r(prefix, _*) => prefix }
res2: String = M9
scala> "M9K9foo" match { case r(prefix, _*) => prefix }
res3: String = M9
scala> val r = """(.*?)(?:(?:A|K)\d.*)?""".r
r: scala.util.matching.Regex = (.*?)(?:(?:A|K)\d.*)?
scala> last match { case r(prefix) => prefix }
res4: String = M9
The diagnosis is the same; there are different ways to pull the string apart.