在斯卡拉实现Scala风格的字符串插值

问题描述:

我想在斯卡拉实现一个Scala风格的字符串插值。下面是一个例子,在斯卡拉实现Scala风格的字符串插值

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

在运行时,我想用一些运行时字符串替换“$ {var1}”和“$ {var2}”。然而,欲以Regex.replaceAllIn时(目标:CharSequence中,替代品:(比赛)⇒字符串),我遇到了以下问题:

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正则表达式中的错误。如果是这样,是否还有其他优雅的方式来实现相同的目标(除了残酷的力量全部替换所有占位符)?

$是替代字符串中特别处理的。这在the documentationreplaceAllIn的描述:

在替换字符串,美元符号($),接着是多个将通过对应于9被解释为对在匹配的模式的一组参考,有数字1前九个小组,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" 
+0

是的,这正是问题所在。谢谢。 – 2014-11-04 22:31:51

+0

对于那些对我所做的字符串插值有兴趣的人。我有一个地图,所有占位符都可以替换。例如, VAL PARAMS =地图( “$ {VAR1}” - > “富”, “$ {VAR2}” - > “栏”) 然后将下面的作品完美地串插 “” “(\ $ \ {\ w + \})”“”。r.replaceAllIn(str,m => params.get(m.matched).getOrElse(“”)) – 2014-11-04 22:40:29

很难说出你期望的行为。问题是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)