2

I want to implement a Scala-style string interpolation in Scala. Here is an example,

val str = "hello ${var1} world ${var2}"

At runtime I want to replace "${var1}" and "${var2}" with some runtime strings. However, when trying to use Regex.replaceAllIn(target: CharSequence, replacer: (Match) ⇒ String), I ran into the following problem:

import scala.util.matching.Regex
val placeholder = new Regex("""(\$\{\w+\})""")
placeholder.replaceAllIn(str, m => s"A${m.matched}B")
java.lang.IllegalArgumentException: No group with name {var1}
  at java.util.regex.Matcher.appendReplacement(Matcher.java:800)
  at scala.util.matching.Regex$Replacement$class.replace(Regex.scala:722)
  at scala.util.matching.Regex$MatchIterator$$anon$1.replace(Regex.scala:700)
  at scala.util.matching.Regex$$anonfun$replaceAllIn$1.apply(Regex.scala:410)
  at scala.util.matching.Regex$$anonfun$replaceAllIn$1.apply(Regex.scala:410)
  at scala.collection.Iterator$class.foreach(Iterator.scala:743)
  at scala.collection.AbstractIterator.foreach(Iterator.scala:1174)
  at scala.util.matching.Regex.replaceAllIn(Regex.scala:410)
  ... 32 elided

However, when I removed '$' from the regular expression, it worked:

val placeholder = new Regex("""(\{\w+\})""")
placeholder.replaceAllIn(str, m => s"A${m.matched}B")
res2: String = hello $A{var1}B world $A{var2}B

So my question is that whether this is a bug in Scala Regex. And if so, are there other elegant ways to achieve the same goal (other than brutal force replaceAllLiterally on all placeholders)?

2 Answers 2

3

$ is a treated specially in the replacement string. This is described in the documentation of replaceAllIn:

In the replacement String, a dollar sign ($) followed by a number will be interpreted as a reference to a group in the matched pattern, with numbers 1 through 9 corresponding to the first nine groups, and 0 standing for the whole match. Any other character is an error. The backslash (\) character will be interpreted as an escape character and can be used to escape the dollar sign. Use Regex.quoteReplacement to escape these characters.

(Actually, that doesn't mention named group references, so I guess it's only sort of documented.)

Anyway, the takeaway here is that you need to escape the $ characters in the replacement string if you don't want them to be treated as references.

new scala.util.matching.Regex("""(\$\{\w+\})""")
    .replaceAllIn("hello ${var1} world ${var2}", m => s"A\\${m.matched}B")
// "hello A${var1}B world A${var2}B"
Sign up to request clarification or add additional context in comments.

2 Comments

Yes, this's exactly the problem. Thanks.
For those who are interested in what exactly I did the string interpolation. I had a map with all placeholders to replace. For example, val params=Map("${var1}"->"foo", "${var2}"->"bar") And then the following works perfectly as the string interpolation """(\$\{\w+\})""".r.replaceAllIn(str, m=>params.get(m.matched).getOrElse(""))
0

It's hard to tell what you're expecting the behavior to do. The issue is that s"${m.matched}" is turning into "${var1}" (and "${var2}"). The '$' is special character to say "place the group with name {var1} here instead".

For example:

scala> placeholder.replaceAllIn(str, m => "$1")
res0: String = hello ${var1} world ${var2}

It replaces the match with the first capturing group (which is m itself).

It's hard to tell exactly what you're doing, but you could escape any $ like so:

scala> placeholder.replaceAllIn(str, m => s"${m.matched.replace("$","\\$")}")
res1: String = hello ${var1} world ${var2}

If what you really want to do is evaluate var1/var2 for some variables in the local scope of the method; that's not possible. In fact, the s"Hello, $name" pattern is actually converted into new StringContext("Hello, ", "").s(name) at compile time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.