在Scala中实现Scala样式的字符串插值

时间:2014-11-04 21:25:00

标签: scala string-interpolation

我想在Scala中实现Scala样式的字符串插值。这是一个例子,

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

在运行时,我想用一些运行时字符串替换“$ {var1}”和“$ {var2}”。但是,当尝试使用Regex.replaceAllIn(目标:CharSequence,replacer :(匹配)⇒字符串)时,我遇到了以下问题:

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

然而,当我从正则表达式中删除'$'时,它起作用了:

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

所以我的问题是,这是否是Scala Regex中的错误。如果是这样,是否有其他优雅的方法来实现相同的目标(除了残酷的力量替换所有占位符的AllLiterally)?

2 个答案:

答案 0 :(得分:3)

$是替换字符串中特别处理的。 replaceAllIn的{​​{3}}中描述了这一点:

  

在替换字符串中,后跟数字的美元符号($)将被解释为对匹配模式中的组的引用,数字1到9对应于前九个组,0表示整个比赛。任何其他字符都是错误的。反斜杠(\)字符将被解释为转义字符,可用于转义美元符号。使用Regex.quoteReplacement来转义这些字符。

(实际上,这并没有提到命名组引用,所以我猜它只记录。)

无论如何,这里要说的是,如果您不希望将它们视为引用,则需要转义替换字符串中的$个字符。

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"

答案 1 :(得分:0)

很难说出你对这种行为的期望。问题是s"${m.matched}"正在转变为"${var1}"(和"${var2}")。 '$'是一个特殊字符,可以说“将名称为{var1}的组放在此处”。

例如:

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

它将匹配替换为第一个捕获组(m本身)。

很难准确地说出你在做什么,但你可以逃避任何这样的$:

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

如果您真正想要做的是为方法的局部范围内的某些变量评估var1 / var2;那是不可能的。实际上,s"Hello, $name"模式实际上在编译时转换为new StringContext("Hello, ", "").s(name)