Scala runtime string interpolation/formatting

前端 未结 3 1937
青春惊慌失措
青春惊慌失措 2021-01-20 11:10

Are there any standard library facilities to do string interpolation/formatting at runtime? I\'d like the formatting to behave exactly the same as the macro based s\"s

3条回答
  •  迷失自我
    2021-01-20 11:51

    There are no silly questions. Only Sunday mornings.

    First, don't use String.format.

    scala> val s = "Count to %d"
    s: String = Count to %d
    
    scala> String format (s, 42)
    :9: error: overloaded method value format with alternatives:
      (x$1: java.util.Locale,x$2: String,x$3: Object*)String 
      (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
    

    But formatting can be expensive. So with your choice of escape handling:

    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.
    

    It turns out that split doesn't quite hack it.

    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
    

    So given

    scala> val r = "\\{}".r
    r: scala.util.matching.Regex = \{}
    
    scala> def parts(s: String) = r split s
    parts: (s: String)Array[String]
    

    Maybe

    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
    

    So

    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
    

    Hey, wait!

    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.
    

    or

    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
    

    or

    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!
    

提交回复
热议问题