Regex pattern matching with a variable number of matching capturing groups - regex

A text file should be parsed line by line, using Scala pattern matching and regular expressions. If a line starts with "names:\t" the subsequent tab-separated names should be provided as a Seq[String] (or something similar).
Here a non-working code example:
val Names = "^names:(?:\t([a-zA-Z0-9_]+))+$".r
"names:\taaa\tbbb\tccc" match {
case Names(names # _*) => println(names)
// […] other cases
case _ => println("no match")
}
Output: List(ccc)
Wanted output: List(aaa, bbb, ccc)
The following code works as desired…
object NamesObject {
private val NamesLine = "^names:\t([a-zA-Z0-9_]+(?:\t[a-zA-Z0-9_]+)*)$".r
def unapplySeq(s: String): Option[Seq[String]] = s match {
case NamesLine(nameString) => Some(nameString.split("\t"))
case _ => None
}
}
"names:\taaa\tbbb\tccc" match {
case NamesObject(names # _*) => println(names)
// […] other cases
case _ => println("no match")
}
Output (as wanted): WrappedArray(aaa, bbb, ccc)
I would like to know: Is this is possible in a simpler manner without creating an object, just like in the first but non-working code example?

Use your working regex.(\w is a predefined character class for[a-zA-Z0-9_])
val Names = """names:\t(\w+(?:\t\w+)*)""".r
"names:\taaa\tbbb\tccc" match {
case Names(names) => println(names.split("\t") toSeq)
case _ => println("no match")
}
With first, second & tail bindings,
val Names = """names:\t(\w+)?\t?(\w+)?\t?((?:\w+?\t?)*)""".r
"names:\taaa\tbbb\tccc\tddd" match {
case Names(first, second, tail) =>
println(first + ", " + second + ", " + (tail.split("\t") toSeq));
case _ => println("no match")
}

As Randall Schulz said, it seems not to be possible just using regular expressions. Therefore the short answer to my question would be no.
My current solution is the following: I use the this class…
class SeparatedLinePattern(Pattern: Regex, separator: String = "\t") {
def unapplySeq(s: String): Option[Seq[String]] = s match {
case Pattern(nameString) => Some(nameString.split(separator).toSeq)
case _ => None
}
}
…to create the patterns:
val Names = new SeparatedLinePattern("""names:\t([A-Za-z]+(?:\t[A-Za-z]+)*)""".r)
val Ints = new SeparatedLinePattern("""ints:\t(\d+(?:\t\d+)*)""".r)
val ValuesWithID = new SeparatedLinePattern("""id-value:\t(\d+\t\w+(?:\t\d+\t\w+)*)""".r)
Here an example how they can be used in a quite flexible manner:
val testStrings = List("names:\taaa", "names:\tbbb\tccc", "names:\tddd\teee\tfff\tggg\thhh",
"ints:\t123", "ints:\t456\t789", "ints:\t100\t200\t300",
"id-value:\t42\tbaz", "id-value:\t2\tfoo\t5\tbar\t23\tbla")
for (s <- testStrings) s match {
case Names(name) => println(s"The name is '$name'")
case Names(a, b) => println(s"The two names are '$a' and '$b'")
case Names(names # _*) => println("Many names: " + names.mkString(", "))
case Ints(a) => println(s"Just $a")
case Ints(a, b) => println(s"$a + $b == ${a.toInt + b.toInt}")
case Ints(nums # _*) => println("Sum is " + (nums map (_.toInt)).sum)
case ValuesWithID(id, value) => println(s"ID of '$value' is $id")
case ValuesWithID(values # _*) => println("As map: " + (values.grouped(2) map (x => x(0).toInt -> x(1))).toMap)
case _ => println("No match")
}
Output:
The name is 'aaa'
The two names are 'bbb' and 'ccc'
Many names: ddd, eee, fff, ggg, hhh
Just 123
456 + 789 == 1245
Sum is 600
ID of 'baz' is 42
As map: Map(2 -> foo, 5 -> bar, 23 -> bla)

Related

Regex scala: Format matching and padding

Correct input format: xxxx/yyyy/zzzz i.e. 4 chars for each part. Total length of the string (not counting "/") should always be 12.
Input can be: xxx/yyy/zzz then it should be padded to come out as 0xxx/0yyy/0zzz
At this stage at least one "/" will be there. If there are 2 parts then we need 6 chars for both.
Looking for a regex with padding logic in Scala.
// line to tune:
val matchThis = raw"(\d{4})/(\d{4})/(\d{4})".r
val valids = List ("1/6", "123456/1", "1/123456", "123456/123456", "1/2/3", "1234/1234/1234", "012/12/3", "1/01/012")
val invalids = List ("/6", "1234567/1", "1/1234567", "1234567/1234567", "/2/3", "1/2/", "12345/1234/1234", "012/12345/3", "1/01/012345")
def tester (input: String) = {
input match {
case matchThis(_*) => "It's valid!"
case _ => "Need some work" /*???*/
}
}
valids.map (s => tester(s))
invalids.map (s => tester(s))
This isn't bulletproof but I think it covers most of what you've described.
val valid = raw"(\d{1,6})/(\d{1,6})(?:/(\d{1,4}))?".r
val output = input match {
case valid(a,b,null) => f"$a%6s/$b%6s" replaceAll(" ","0")
case valid(a,b,c) => f"$a%4s/$b%4s/$c%4s" replaceAll(" ","0")
case _ => "invalid"
}
A little more complete.
val valid = raw"(\d{1,4})/(\d{1,4})/(\d{1,4})|(\d{1,6})/(\d{1,6})".r
val output = input match {
case valid(null,null,null,a,b) => f"$a%6s/$b%6s" replaceAll(" ","0")
case valid(a,b,c,null,null) => f"$a%4s/$b%4s/$c%4s" replaceAll(" ","0")
case _ => "invalid"
}

Scala match statements with inline regexes

I am trying (if at all possible) to get a Scala match/case statement to perform an inline regex match for me.
Specifically, I have a method that will run a match, and if the input to the method starts with the string "fizz", then I would like the match statement to select the correct case:
def animalToSound(animal : String) : String = {
animal match {
case "duck" => "quack"
case "lion" => "roar"
case "dog" => "woof"
case matchesFizzRegex(animal) => "heyo!"
case _ => "meow"
}
}
def matchesFizzRegex(animal : String) : ??? = {
val fizzRegex = "fizz*".r
if(fizzRegex.match(animal)) {
???
} else {
???
}
}
So if I call animalToSound("fizzBuzz"), then the desired behavior is:
Does "fizzBuzz" equal "duck"? No. So try the next case.
Does "fizzBuzz" equal "lion"? No. So try the next case.
Does "fizzBuzz" equal "dog"? No. So try the next case.
Does "fizzBuzz" match the fizz regex (any string starting with 'fizz')? Yes, so return "heyo!"
Any ideas how I can get this working properly?
Simple and straight forward
Use pattern matching with guards and matches method of string
def animalToSound(animal : String) : String = animal match {
case "duck" => "quack"
case "lion" => "roar"
case "dog" => "woof"
case x if x matches "fizz.*" => "heyo!"
case _ => "meow"
}
You can match regex among other cases:
val reg = "fizz.*".r
animal match {
case "duck" => "quack"
case "lion" => "roar"
case "dog" => "woof"
case `reg` => "heyo!"
case _ => "meow"
}

Matching or regular expression in scala

I have a regular expression like ".*(A20).*|.*(B30).*|C".
I would like to write a function its returns A20 or B30 based on the match found.
val regx=".*(A20).*|.*(B30).*".r
"helloA20" match { case regx(b,_) => b; case _ => "" } // A20
"helloB30" match { case regx(b,_) => b; case _ => "" } // null
"C" match { case regx(b,_) => b case _ => "" }
It's returning null because I am not considering the second group. In my actual code, I have a lot of group like that. I would like to return the matched string. Please help me to find a solution.
Easy! It should be like this:
val regx="^(.*(B30|A20).*|(C))$".r
Demo: https://regex101.com/r/nA6dQ9/1
Then you get the second value in the array of every group.
That way you only have one group regardless of the many possibilities.
You're close:
def extract(s: String) = s match {
case regx(b, _) if b != null => b
case regx(_, b) if b != null => b
case _ => ""
}
extract("helloA20")
res3: String = A20
extract("helloB30")
res4: String = B30
extract("A30&B30")
res6: String = B30
If you have a lot of groups, it's reasonable to use for comprehension insted of pattern matching. This code will return first match or None:
val letters = ('A' to 'Z').toSeq
val regex = letters.map(_.toString).mkString("(", "|", ")").r
def extract(s: String) = {
for {
m <- regex.findFirstMatchIn(s)
} yield m.group(1)
}
extract("A=B=")
extract("dsfdsBA")
extract("C====b")
extract("a====E")
res0: Option[String] = Some(A)
res1: Option[String] = Some(B)
res2: Option[String] = Some(C)
res3: Option[String] = Some(E)

Do parenthesized groups work in Scala?

Parentheses in regular expressions don't seem to work in match/case statements. For example, the following code
val pat1 = """ab""".r
val pat2 = """(a)(b)""".r
val pat3 = """((a)(b))""".r
val pat4 = """((a)b)""".r
val pat5 = """(ab)""".r
"ab" match {
case pat1(x) => println("1 " + x)
case pat2(x) => println("2 " + x)
case pat3(x) => println("3 " + x)
case pat4(x) => println("4 " + x)
case pat5(x) => println("5 " + x)
case _ => println("None of the above")
}
prints "5 ab", but I would have expected any of the patterns to match. I'd like to use "(...)?" optional elements, but I can't. Related to this, I can't get (?m) to work. My patterns work okay outside of a match/case expression. Can someone explain to me how Scala handles regular expressions in match/case expressions?
I'm trying to write a tokenizer in Scala
Regex defines unapplySeq, not unapply, which means that you get each group in its own variable. Also, although lower-case matchers may work in some instances (i.e. with parameters), you really should use upper-case. So, what will work is:
val Pat1 = """ab""".r
val Pat2 = """(a)(b)""".r
val Pat3 = """((a)(b))""".r
val Pat4 = """((a)b)""".r
val Pat5 = """(ab)""".r
def no() { println("No match") }
"ab" match { case Pat1() => println("Pat1"); case _ => no }
"ab" match { case Pat2(x,y) => println("Pat2 "+x+" "+y); case _ => no }
"ab" match { case Pat3(x,y,z) => println("Pat3 "+x+" "+y+" "+z); case _ => no }
"ab" match { case Pat4(x,y) => println("Pat4 "+x+" "+y); case _ => no }
"ab" match { case Pat5(x) => println("Pat5 "+x); case _ => no }
(You will always get a match.)
If you want all matches, use # _*
"ab" match { case Pat3(w # _*) => println(w); case _ => no }
I'm not sure what you mean by (?a) so I don't know what's wrong with it. Don't confuse (?a) with (?:a) (or with (a?) or with (a)?).
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))
}
Test this demo here.

How to pattern match using regular expression in Scala?

I would like to be able to find a match between the first letter of a word, and one of the letters in a group such as "ABC". In pseudocode, this might look something like:
case Process(word) =>
word.firstLetter match {
case([a-c][A-C]) =>
case _ =>
}
}
But how do I grab the first letter in Scala instead of Java? How do I express the regular expression properly? Is it possible to do this within a case class?
You can do this because regular expressions define extractors but you need to define the regex pattern first. I don't have access to a Scala REPL to test this but something like this should work.
val Pattern = "([a-cA-C])".r
word.firstLetter match {
case Pattern(c) => c bound to capture group here
case _ =>
}
Since version 2.10, one can use Scala's string interpolation feature:
implicit class RegexOps(sc: StringContext) {
def r = new util.matching.Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*)
}
scala> "123" match { case r"\d+" => true case _ => false }
res34: Boolean = true
Even better one can bind regular expression groups:
scala> "123" match { case r"(\d+)$d" => d.toInt case _ => 0 }
res36: Int = 123
scala> "10+15" match { case r"(\d\d)${first}\+(\d\d)${second}" => first.toInt+second.toInt case _ => 0 }
res38: Int = 25
It is also possible to set more detailed binding mechanisms:
scala> object Doubler { def unapply(s: String) = Some(s.toInt*2) }
defined module Doubler
scala> "10" match { case r"(\d\d)${Doubler(d)}" => d case _ => 0 }
res40: Int = 20
scala> object isPositive { def unapply(s: String) = s.toInt >= 0 }
defined module isPositive
scala> "10" match { case r"(\d\d)${d # isPositive()}" => d.toInt case _ => 0 }
res56: Int = 10
An impressive example on what's possible with Dynamic is shown in the blog post Introduction to Type Dynamic:
object T {
class RegexpExtractor(params: List[String]) {
def unapplySeq(str: String) =
params.headOption flatMap (_.r unapplySeq str)
}
class StartsWithExtractor(params: List[String]) {
def unapply(str: String) =
params.headOption filter (str startsWith _) map (_ => str)
}
class MapExtractor(keys: List[String]) {
def unapplySeq[T](map: Map[String, T]) =
Some(keys.map(map get _))
}
import scala.language.dynamics
class ExtractorParams(params: List[String]) extends Dynamic {
val Map = new MapExtractor(params)
val StartsWith = new StartsWithExtractor(params)
val Regexp = new RegexpExtractor(params)
def selectDynamic(name: String) =
new ExtractorParams(params :+ name)
}
object p extends ExtractorParams(Nil)
Map("firstName" -> "John", "lastName" -> "Doe") match {
case p.firstName.lastName.Map(
Some(p.Jo.StartsWith(fn)),
Some(p.`.*(\\w)$`.Regexp(lastChar))) =>
println(s"Match! $fn ...$lastChar")
case _ => println("nope")
}
}
As delnan pointed out, the match keyword in Scala has nothing to do with regexes. To find out whether a string matches a regex, you can use the String.matches method. To find out whether a string starts with an a, b or c in lower or upper case, the regex would look like this:
word.matches("[a-cA-C].*")
You can read this regex as "one of the characters a, b, c, A, B or C followed by anything" (. means "any character" and * means "zero or more times", so ".*" is any string).
To expand a little on Andrew's answer: The fact that regular expressions define extractors can be used to decompose the substrings matched by the regex very nicely using Scala's pattern matching, e.g.:
val Process = """([a-cA-C])([^\s]+)""".r // define first, rest is non-space
for (p <- Process findAllIn "aha bah Cah dah") p match {
case Process("b", _) => println("first: 'a', some rest")
case Process(_, rest) => println("some first, rest: " + rest)
// etc.
}
String.matches is the way to do pattern matching in the regex sense.
But as a handy aside, word.firstLetter in real Scala code looks like:
word(0)
Scala treats Strings as a sequence of Char's, so if for some reason you wanted to explicitly get the first character of the String and match it, you could use something like this:
"Cat"(0).toString.matches("[a-cA-C]")
res10: Boolean = true
I'm not proposing this as the general way to do regex pattern matching, but it's in line with your proposed approach to first find the first character of a String and then match it against a regex.
EDIT:
To be clear, the way I would do this is, as others have said:
"Cat".matches("^[a-cA-C].*")
res14: Boolean = true
Just wanted to show an example as close as possible to your initial pseudocode. Cheers!
First we should know that regular expression can separately be used. Here is an example:
import scala.util.matching.Regex
val pattern = "Scala".r // <=> val pattern = new Regex("Scala")
val str = "Scala is very cool"
val result = pattern findFirstIn str
result match {
case Some(v) => println(v)
case _ =>
} // output: Scala
Second we should notice that combining regular expression with pattern matching would be very powerful. Here is a simple example.
val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
"2014-11-20" match {
case date(year, month, day) => "hello"
} // output: hello
In fact, regular expression itself is already very powerful; the only thing we need to do is to make it more powerful by Scala. Here are more examples in Scala Document: http://www.scala-lang.org/files/archive/api/current/index.html#scala.util.matching.Regex
Note that the approach from #AndrewMyers's answer matches the entire string to the regular expression, with the effect of anchoring the regular expression at both ends of the string using ^ and $. Example:
scala> val MY_RE = "(foo|bar).*".r
MY_RE: scala.util.matching.Regex = (foo|bar).*
scala> val result = "foo123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = foo
scala> val result = "baz123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = No match
scala> val result = "abcfoo123" match { case MY_RE(m) => m; case _ => "No match" }
result: String = No match
And with no .* at the end:
scala> val MY_RE2 = "(foo|bar)".r
MY_RE2: scala.util.matching.Regex = (foo|bar)
scala> val result = "foo123" match { case MY_RE2(m) => m; case _ => "No match" }
result: String = No match