68

With too many arguments, String.format easily gets too confusing. Is there a more powerful way to format a String. Like so:

"This is #{number} string".format("number" -> 1)

Or is this not possible because of type issues (format would need to take a Map[String, Any], I assume; don’t know if this would make things worse).

Or is the better way doing it like this:

val number = 1
<plain>This is { number } string</plain> text

even though it pollutes the name space?

Edit:

While a simple pimping might do in many cases, I’m also looking for something going in the same direction as Python’s format() (See: http://docs.python.org/release/3.1.2/library/string.html#formatstrings)

Sven Hohenstein
  • 80,497
  • 17
  • 145
  • 168
Debilski
  • 66,976
  • 12
  • 110
  • 133
  • FWIW, I disagree that one ever needs to be in a situation where `String.format` has too many arguments. Use two or more strings; concat with `+`. But of course it would be nice if someone took the time to clone the python format for those people who prefer that style to the C style. – Rex Kerr Oct 29 '10 at 15:31
  • Sure, there are not so many applications but sometimes you just want to externalise a template string and then the advantage is evident in my opinion. Or for internationalisation stuff (though I don’t know if e.g. even the Python format is powerful enough for that). – Debilski Oct 29 '10 at 15:46
  • 1
    But shouldn’t there be such a thing for Java already? – Debilski Oct 29 '10 at 15:47
  • @Always-Asking (via [suggested edit](http://stackoverflow.com/review/suggested-edits/6219208)): Those tags probably weren't in existence when this question was posted; the addition does make sense though. – Pokechu22 Nov 13 '14 at 01:12

7 Answers7

88

In Scala 2.10 you can use string interpolation.

val height = 1.9d
val name = "James"
println(f"$name%s is $height%2.2f meters tall")  // James is 1.90 meters tall
Andrej Herich
  • 3,246
  • 27
  • 14
46

Well, if your only problem is making the order of the parameters more flexible, this can be easily done:

scala> "%d %d" format (1, 2)
res0: String = 1 2

scala> "%2$d %1$d" format (1, 2)
res1: String = 2 1

And there's also regex replacement with the help of a map:

scala> val map = Map("number" -> 1)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map((number,1))

scala> val getGroup = (_: scala.util.matching.Regex.Match) group 1
getGroup: (util.matching.Regex.Match) => String = <function1>

scala> val pf = getGroup andThen map.lift andThen (_ map (_.toString))
pf: (util.matching.Regex.Match) => Option[java.lang.String] = <function1>

scala> val pat = "#\\{([^}]*)\\}".r
pat: scala.util.matching.Regex = #\{([^}]*)\}

scala> pat replaceSomeIn ("This is #{number} string", pf)
res43: String = This is 1 string
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
18

You can easily implement a richer formatting yourself (with the "enhance my library" approach):

scala> implicit def RichFormatter(string: String) = new {
     |   def richFormat(replacement: Map[String, Any]) =
     |     (string /: replacement) {(res, entry) => res.replaceAll("#\\{%s\\}".format(entry._1), entry._2.toString)}
     | }
RichFormatter: (string: String)java.lang.Object{def richFormat(replacement: Map[String,Any]): String}

scala> "This is #{number} string" richFormat Map("number" -> 1)
res43: String = This is 1 string

Or on more recent Scala versions since the original answer:

implicit class RichFormatter(string: String) {
  def richFormat(replacement: Map[String, Any]): String =
    replacement.foldLeft(string) { (res, entry) =>
      res.replaceAll("#\\{%s\\}".format(entry._1), entry._2.toString)
    }
}
Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30
Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81
  • True, I might even use `%` for that (as in some other languages). But still, it could be a bit more powerful… (Like python’s format maybe.) – Debilski Oct 29 '10 at 11:55
17

This the answer I came here looking for:

"This is %s string".format(1)
Priyank Desai
  • 3,693
  • 1
  • 27
  • 17
17

Maybe the Scala-Enhanced-Strings-Plugin can help you. Look here:

Scala-Enhanced-Strings-Plugin Documentation

Ruediger Keller
  • 3,024
  • 2
  • 20
  • 17
  • Looks pretty interesting. (Both the docu and the plugin.) But still a bit crazy (also both) because the syntax does not use any delimiters sometimes… – Debilski Oct 29 '10 at 13:05
  • I'm using it in production and it's great! Delimiters are only required when the expression to be interpolated has much syntax beyond just a variable name. – Alex Cruise Oct 29 '10 at 17:32
  • For 2.10+ users, definitely check out the built in string interpolation at http://docs.scala-lang.org/overviews/core/string-interpolation.html. Thanks to Andrej for pointing this out. – emragins May 11 '13 at 00:11
5

If you're using 2.10 then go with built-in interpolation. Otherwise, if you don't care about extreme performance and are not afraid of functional one-liners, you can use a fold + several regexp scans:

val template = "Hello #{name}!"
val replacements = Map( "name" -> "Aldo" )
replacements.foldLeft(template)((s:String, x:(String,String)) => ( "#\\{" + x._1 + "\\}" ).r.replaceAllIn( s, x._2 ))
Aldo Bucchi
  • 425
  • 5
  • 10
3

You might also consider the use of a template engine for really complex and long strings. On top of my head I have Scalate which implements amongst others the Mustache template engine.

Might be overkill and performance loss for simple strings, but you seem to be in that area where they start becoming real templates.

Dominik Bucher
  • 1,509
  • 13
  • 16