如何参数化 StringContext 使用的格式字符串?
How do I parameterize format strings used by StringContext?
假设我有这样一行代码:
log.info(f"$name%-20s $amt%7.2f")
我想要的是使 20
(name
的宽度)本身可以参数化。我该怎么做?
如果我还想使 amt
的 7.2
宽度也参数化怎么办?这可能吗?
[编辑:我希望将 20
和 7.2
之类的东西放在变量中,这样它们就不会硬编码在格式字符串中。]
我试过直接使用 StringContext
,但似乎我不明白发生在什么宏扩展中:
StringContext(...).f(...)
你离事实不远:
val amt= 1234.1234d
val name = "James"
println(f"|$name%20s $amt%7.2f|")
产量:
| James 1234,12|
关于将 20 和 7.2 作为参数传递:模板可以生成为:
val len = 20
val prec = 7.2
val template = s"$$name%${len}s $$amt%${prec}f"
产生:
$name%20 $amt%7.2f
但是字符串插值似乎不是解决方案,因为即使脱糖到 StringContext 中,它也是在编译时完成的,然后
s"$template
产量 $name%20s $amt%7.2f
.
我想我已经用另一种语言工作了
您可以实现您的 StringContext
注意:s"a=${a},b=${b}"
为StringContext("a=",",b=").s(a,b)
case class Format(t: String)
// this implement is not type safe in Compile time,
implicit class MyFormat(val sc: StringContext) {
def myFormat(args: Any*) = {
val partIter = sc.parts.iterator
var stringFormatArgs = List.empty[Object]
val sb = new StringBuilder(partIter.next())
def impl(list: List[Any]): Unit = {
list match {
case (_: Format) :: tail =>
throw new Exception("")
case (any: Object) :: (format: Format) :: tail =>
sb.append(partIter.next()).append(format.t).append(partIter.next())
stringFormatArgs = any :: stringFormatArgs
impl(tail)
case (any: Any) :: tail =>
sb.append(any.toString).append(partIter.next())
impl(tail)
case Nil =>
}
}
impl(args.toList)
String.format(sb.toString(), stringFormatArgs.reverse: _*)
}
}
// 测试
val amt = 1234.1234d
val name = "James"
val len = Format("20s")
val prec = Format("7.2f")
myFormat"$name%$len,$amt%$prec," //" James,1234.12,"
你可以分两部分完成。
val name = "Jan"
val nLen = -10 //name length
val amt = 1.1
val ff = 5.3 //float format
s"%${nLen}s".format(name) + s"%${ff}f".format(amt)
//res0: String = Jan 1.100
或者一次通过。
s"%${nLen}s%${ff}f".format(name, amt)
//res1: String = Jan 1.100
假设我有这样一行代码:
log.info(f"$name%-20s $amt%7.2f")
我想要的是使 20
(name
的宽度)本身可以参数化。我该怎么做?
如果我还想使 amt
的 7.2
宽度也参数化怎么办?这可能吗?
[编辑:我希望将 20
和 7.2
之类的东西放在变量中,这样它们就不会硬编码在格式字符串中。]
我试过直接使用 StringContext
,但似乎我不明白发生在什么宏扩展中:
StringContext(...).f(...)
你离事实不远:
val amt= 1234.1234d
val name = "James"
println(f"|$name%20s $amt%7.2f|")
产量:
| James 1234,12|
关于将 20 和 7.2 作为参数传递:模板可以生成为:
val len = 20
val prec = 7.2
val template = s"$$name%${len}s $$amt%${prec}f"
产生:
$name%20 $amt%7.2f
但是字符串插值似乎不是解决方案,因为即使脱糖到 StringContext 中,它也是在编译时完成的,然后
s"$template
产量 $name%20s $amt%7.2f
.
我想我已经用另一种语言工作了
您可以实现您的 StringContext
注意:s"a=${a},b=${b}"
为StringContext("a=",",b=").s(a,b)
case class Format(t: String)
// this implement is not type safe in Compile time,
implicit class MyFormat(val sc: StringContext) {
def myFormat(args: Any*) = {
val partIter = sc.parts.iterator
var stringFormatArgs = List.empty[Object]
val sb = new StringBuilder(partIter.next())
def impl(list: List[Any]): Unit = {
list match {
case (_: Format) :: tail =>
throw new Exception("")
case (any: Object) :: (format: Format) :: tail =>
sb.append(partIter.next()).append(format.t).append(partIter.next())
stringFormatArgs = any :: stringFormatArgs
impl(tail)
case (any: Any) :: tail =>
sb.append(any.toString).append(partIter.next())
impl(tail)
case Nil =>
}
}
impl(args.toList)
String.format(sb.toString(), stringFormatArgs.reverse: _*)
}
}
// 测试
val amt = 1234.1234d
val name = "James"
val len = Format("20s")
val prec = Format("7.2f")
myFormat"$name%$len,$amt%$prec," //" James,1234.12,"
你可以分两部分完成。
val name = "Jan"
val nLen = -10 //name length
val amt = 1.1
val ff = 5.3 //float format
s"%${nLen}s".format(name) + s"%${ff}f".format(amt)
//res0: String = Jan 1.100
或者一次通过。
s"%${nLen}s%${ff}f".format(name, amt)
//res1: String = Jan 1.100