Giovanni Botta
Giovanni Botta

Reputation: 9816

Regex group match and replacement in a single statement

I am using a code generation tool and I need to convert method calls like this (it's java):

public MyObjectType MyObjectType(){

into a more java-fied version:

public MyObjectType myObjectType(){

I have this working snippet of Scala code that does that:

val pattern = """[\s]+public[\s]+[\w]+[\s]+([\w]+\(\))\{""".r

val tmp = pattern.findAllIn(s).matchData map { m =>
  val x = m.group(1).replaceAll("\\(\\)", "\\\\(\\\\)")
  s.replaceAll(x, firstLowerCase(x))
}
// if there is no match, return unmodified string,
// otherwise return the only match
val converted = if (tmp.isEmpty) s else tmp.next()

Where s is one line of code, possible candidate for the transformation.

The function firstLowerCase is defined as:

def firstLowerCase(s: String): String = {
  val (first, rest) = s.splitAt(1)
  first.toLowerCase + rest
}

The code above works fine, but I was wondering if it was possible to slim it down a bit (ideally to a single pattern match statement), e.g., by (somehow) removing the explicit conversion of () to \(\), doing the match and replace in one shot or incorporating the above function in the replacement. I am not a regex expert so maybe this is too much to ask. Ideas?

Upvotes: 2

Views: 1244

Answers (2)

Erik Kaplun
Erik Kaplun

Reputation: 38217

This seems to be doing what you need. I've also made the pattern a bit more flexible e.g. in that spaces are allowed before the {.

import scala.util.matching.Regex

// wingedsubmariner's answer actually has a better pattern
val Pattern = """(public\s+\w+\s+)(\w+)(\s*\(\)\s*\{)""".r

def firstLowerCase(s: String): String = {
  val (first, rest) = s.splitAt(1)
  first.toLowerCase + rest
}

val lines = List(
  "public MyObjectType MyObjectTypeMeth(){",
  " public MyObjectType MyObjectTypeMeth(){   ",
  "public MyObjectType MyObjectTypeMeth() {",
  "public MyObjectType MyObjectTypeMeth () {")

lines.foreach { s =>
  val converted = Pattern.replaceAllIn(s, m => {
    m.group(1) + firstLowerCase(m.group(2)) + m.group(3)
  })

  println(converted)
}

output:

public MyObjectType myObjectTypeMeth(){
 public MyObjectType myObjectTypeMeth(){   
public MyObjectType myObjectTypeMeth() {
public MyObjectType myObjectTypeMeth () {

Upvotes: 1

wingedsubmariner
wingedsubmariner

Reputation: 13667

Try this:

val pattern = """(public\s+\w+\s+)(\w)(?=\w*\(\)\{)""".r
val converted = pattern.replaceAllIn(s, m => m.group(1) + m.group(2).toLowerCase)

replaceAllIn makes it unnecessary to do a findAllIn and then go back and do a replaceAll. We match the beginning part of the pattern, the character we need to lower case, and then handle the rest with a look ahead assertion (we don't need to replace it, only confirm that it is present). The replacement text is the beginning of the pattern and that single character made lowercase, firstLowerCase isn't needed.

Upvotes: 5

Related Questions