Scala运行时字符串插值/格式化

时间:2014-03-25 18:16:19

标签: string scala

是否有任何标准库工具可以在运行时进行字符串插值/格式化?我希望格式化与基于宏的s"scala ${implementation}完全相同,只是我的字符串格式是在运行时从配置文件加载的。

val format = config.getString("my.key")
val stringContext = parseFormat(format)
val res = stringContext.f("arg1", "arg2")

parseFormat返回StringContext

我想,最坏的情况是,我可以将字符串拆分为" {}"序列并使用这些部分来构造StringContext。

// untested
def parseFormat(format: String): StringContext =
  new StringContext("""{}""".r.split(format): _*)

是否有一个明显的解决方案,我错过了或者上面的黑客会不会这样做?

3 个答案:

答案 0 :(得分:2)

没有愚蠢的问题。只有周日早上。

首先,don't use String.format

scala> val s = "Count to %d"
s: String = Count to %d

scala> String format (s, 42)
<console>:9: error: overloaded method value format with alternatives:
  (x$1: java.util.Locale,x$2: String,x$3: Object*)String <and>
  (x$1: String,x$2: Object*)String
 cannot be applied to (String, Int)
              String format (s, 42)
                     ^

scala> s format 42
res1: String = Count to 42

但格式化可能很昂贵。因此,您可以选择逃生处理:

scala> StringContext("Hello, {}. Today is {}." split "\\{}" : _*).s("Bob", "Tuesday")
res2: String = Hello, Bob. Today is Tuesday.

scala> StringContext("""Hello, \"{}.\" Today is {}.""" split "\\{}" : _*).s("Bob", "Tuesday")
res3: String = Hello, "Bob." Today is Tuesday.

scala> StringContext("""Hello, \"{}.\" Today is {}.""" split "\\{}" : _*).raw("Bob", "Tuesday")
res4: String = Hello, \"Bob.\" Today is Tuesday.

事实证明,拆分并不会破坏它。

scala> StringContext("Count to {}" split "\\{}" : _*) s 42
java.lang.IllegalArgumentException: wrong number of arguments (1) for interpolated string with 1 parts
  at scala.StringContext.checkLengths(StringContext.scala:65)
  at scala.StringContext.standardInterpolator(StringContext.scala:121)
  at scala.StringContext.s(StringContext.scala:94)
  ... 33 elided

所以给出

scala> val r = "\\{}".r
r: scala.util.matching.Regex = \{}

scala> def parts(s: String) = r split s
parts: (s: String)Array[String]

也许

scala> def f(parts: Seq[String], args: Any*) = (parts zip args map (p => p._1 + p._2)).mkString
f: (parts: Seq[String], args: Any*)String

所以

scala> val count = parts("Count to {}")
count: Array[String] = Array("Count to ")

scala> f(count, 42)
res7: String = Count to 42

scala> f(parts("Hello, {}. Today is {}."), "Bob", "Tuesday")
res8: String = Hello, Bob. Today is Tuesday
嘿,等等!

scala> def f(parts: Seq[String], args: Any*) = (parts.zipAll(args, "", "") map (p => p._1 + p._2)).mkString
f: (parts: Seq[String], args: Any*)String

scala> f(parts("Hello, {}. Today is {}."), "Bob", "Tuesday")
res9: String = Hello, Bob. Today is Tuesday.

scala> def f(parts: Seq[String], args: Any*) = (for (i <- 0 until (parts.size max args.size)) yield (parts.applyOrElse(i, (_: Int) => "") + args.applyOrElse(i, (_: Int) => ""))).mkString
f: (parts: Seq[String], args: Any*)String

scala> def f(parts: Seq[String], args: Any*) = { val sb = new StringBuilder ; for (i <- 0 until (parts.size max args.size) ; ss <- List(parts, args)) { sb append ss.applyOrElse(i, (_: Int) => "") } ; sb.toString }
f: (parts: Seq[String], args: Any*)String

scala> f(parts("Hello, {}. Today is {}. {}"), "Bob", "Tuesday", "Bye!")
res16: String = Hello, Bob. Today is Tuesday. Bye!

答案 1 :(得分:0)

一个。从Scala 2.10.3开始,除非您知道编译时参数的数量,否则不能使用StringContext.f,因为.f方法是一个宏。

B中。使用String.format,就像在Java的好日子里一样。

答案 2 :(得分:0)

我有一个类似的要求,我从配置文件加载Seq[String],这将成为要执行的命令(使用scala.sys.process)。为了简化格式并忽略任何潜在的转义问题,我还将变量名称设置为可配置选项。

配置看起来像这样:

command = ["""C:\Program Files (x86)\PuTTY\pscp.exe""", "-P", "2222", "-i",
          ".vagrant/machines/default/virtualbox/private_key", "$source", "~/$target"]
source = "$source"
target = "$target"

我无法找到使用StringContext"string".format的好方法(或有效方式),因此我推出了自己的VariableCommand,这与{{1}非常相似但是,单个变量在任何顺序和任何项目中都可以出现零次或多次。

基本思想是创建一个函数,该函数接受变量值,然后将参与字符串(例如StringContext)或重复获取变量值(例如"~/")来构建结果(例如"test.conf")。这个函数创建一次,这是所有复杂性的地方,然后在替换时它非常简单(并且希望快,虽然我还没有完成任何性能测试,或者根本没有进行大量测试)。

对于那些可能想知道为什么我这样做的人来说,它是使用ansible(它不支持Windows控制机器)进行自动化测试跨平台进行配置。这允许我将文件复制到目标机器并在本地运行ansible。