1. 程式人生 > >Scala字符串插值 - StringContext

Scala字符串插值 - StringContext

eight strings 字符 中繼 函數 target ash iterator 建立

翻譯自:STRING INTERPOLATION

簡介

自2.10.0版本開始,Scala提供了一種新的機制來根據數據生成字符串:字符串插值。字符串插值允許使用者將變量引用直接插入處理過的字面字符中。如下例:

val name="James"
println(s"Hello,$name")//Hello,James

在上例中, s”Hello,$name” 是待處理字符串字面,編譯器會對它做額外的工作。待處理字符串字面通過“號前的字符來標示(例如:上例中是s)。字符串插值的實現細節在 SIP-11 中有全面介紹。

用法

Scala 提供了三種創新的字符串插值方法:s,f 和 raw.

s 字符串插值器

在任何字符串前加上s,就可以直接在串中使用變量了。你已經見過這個例子:

val name="James"
println(s"Hello,$name")//Hello,James 此例中,$name嵌套在一個將被s字符串插值器處理的字符串中。插值器知道在這個字符串的這個地方應該插入這個name變量的值,以使輸出字符串為Hello,James。使用s插值器,在這個字符串中可以使用任何在處理範圍內的名字。

字符串插值器也可以處理任意的表達式。例如:

println(s"1+1=${1+1}") 將會輸出字符串1+1=2。任何表達式都可以嵌入到${}中。

f 插值器

在任何字符串字面前加上 f,就可以生成簡單的格式化串,功能相似於其他語言中的 printf 函數。當使用 f 插值器的時候,所有的變量引用都應當後跟一個printf-style格式的字符串,如%d。看下面這個例子:

val height=1.9d
val name="James"
println(f"$name%s is $height%2.2f meters tall")//James is 1.90 meters tall f 插值器是類型安全的。如果試圖向只支持 int 的格式化串傳入一個double 值,編譯器則會報錯。例如:

val height:Double=1.9d

scala>f"$height%4d"
<console>:9: error: type mismatch;
 found : Double
 required: Int
           f"$height%4d"
              ^ f 插值器利用了java中的字符串數據格式。這種以%開頭的格式在 [Formatter javadoc] 中有相關概述。如果在具體變量後沒有%,則格式化程序默認使用 %s(串型)格式。

raw 插值器

除了對字面值中的字符不做編碼外,raw 插值器與 s 插值器在功能上是相同的。如下是個被處理過的字符串:

scala>s"a\nb"
res0:String=
a
b 這裏,s 插值器用回車代替了\n。而raw插值器卻不會如此處理。

scala>raw"a\nb"
res1:String=a\nb 當不想輸入\n被轉換為回車的時候,raw 插值器是非常實用的。

除了以上三種字符串插值器外,使用者可以自定義插值器。

高級用法

在Scala中,所有處理過的字符串字面值都進行了簡單編碼轉換。任何時候編譯器遇到一個如下形式的字符串字面值:

id"string content" 它都會被轉換成一個StringContext實例的call(id)方法。這個方法在隱式範圍內仍可用。只需要簡單得 建立一個隱類,給StringContext實例增加一個新方法,便可以定義我們自己的字符串插值器。如下例:

//註意:為了避免運行時實例化,我們從AnyVal中繼承。
//更多信息請見值類的說明
implicit class JsonHelper(val sc:StringContext) extends AnyVal{
  def json(args:Any*):JSONObject=sys.error("TODO-IMPLEMENT")
}

def giveMeSomeJson(x:JSONObject):Unit=...

giveMeSomeJson(json"{name:$name,id:$id}") 在這個例子中,我們試圖通過字符串插值生成一個JSON文本語法。隱類 JsonHelper 作用域內使用該語法,且這個JSON方法需要一個完整的實現。只不過,字符串字面值格式化的結果不是一個字符串,而是一個JSON對象。

當編譯器遇到”{name:$name,id:$id”}”,它將會被重寫成如下表達式:

new StringContext("{name:",",id:","}").json(name,id)

隱類則被重寫成如下形式

new JsonHelper(new StringContext("{name:",",id:","}")).json(name,id)

所以,JSON方法可以訪問字符串的原生片段而每個表達式都是一個值。這個方法的一個簡單但又令人迷惑的例子:

implicit class JsonHelper(val sc:StringContext) extends AnyVal{
  def json(args:Any*):JSONObject={
    val strings=sc.parts.iterator
    val expressions=args.iterator
    var buf=new StringBuffer(strings.next)
    while(strings.hasNext){
      buf append expressions.next
      buf append strings.next
    }
    parseJson(buf)
  }
}

被處理過的字符串的每部分都是StringContext的成員。每個表達式的值都將傳入到JSON方法的args參數。JSON方法接受這些值並合成一個大字符串,然後再解析成JSON格式。有一種更復雜的實現可以避免合成字符串的操作,它只是簡單的直接通過原生字符串和表達式值構建JSON。

限制

字符串插值目前對模式匹配語句不適用。此特性將在2.11版本中生效。

Scala字符串插值 - StringContext