String


在Scala中,重用了Java的字串類別java.lang.String,所以基本上,Scala中字串與Java中的字串有著相同的特性(可以參考  那些字串二三事)。你可以在Scala中直接用雙引號宣告字串,或者是使用字元陣列來建構字串,例如:
val str1 = "Scala"
val str2 = new String(Array('S', 'c', 'a', 'l', 'a'))
println(str1) // 顯示 Scala
println(str2) // 顯示 Scala

字串是不可變動的(Immutable),在建立之後就不能改變它的值,在內部字串是使用陣列儲存字元資料,你可以根據索引取得字串中的字元值,基本上你可以使用charAt方法,索引由0開始,例如str1.charAt(0)傳回'S'字元,str1.charAt(1)傳回'c字元,',以下是幾個與索引相關的方法(直接引自java.lang.String的方法宣告方式):
char charAt(int index) 傳回指定索引處的字元
int indexOf(int ch) 傳回指定字元第一個找到的索引位置
int indexOf(String str) 傳回指定字串第一個找到的索引位置
int lastIndexOf(int ch) 傳回指定字元最後一個找到的索引位置
String substring(int beginIndex) 取出指定索引處至字串尾端的子字串
String substring(int beginIndex, int endIndex) 取出指定索引範圍子字串
char[] toCharArray() 將字串轉換為字元陣列


要取得字串長度資訊,也是透過String類別的length方法來取得,簡單來說,只要查詢 java.lang.String 的API,就可以知道在Scala中,字串有哪些方法可以操作。

除 了使用Java的方式來操作字串之外,在Scala中,你還可以對字串作更多的操作,例如,有剖析字串,可以使用toInt、toLong、 toDouble等方法(在Java中則是使用對應Wrapper類別的parseXXX()方法),也可以將某個字串「乘以」某數,表示重複字串幾次:
println("123".toInt + "456".toInt) // 顯示 579
println("Scala" * 3) // 顯示 ScalaScalaScala

若要循序取出字串中每個字元,可以使用 for 運算式
for(c <- "Scala") {
println(c) // 每行顯示一個字元
}

簡單的函式 曾介紹過的foreach方法,在字串上也可以使用:
"Scala".foreach((c: Char) => println(c)) // 每行顯示一個字元

事實上,如果你要指定索引,取得字串中的字元,除了charAt方法之外,使用以下的方式會更為直覺:
val str = "Scala"
println(str(0)) // 顯示 'S'
println(str(1)) // 顯示 'c'
println(str(2)) // 顯示 'a'
println(str(3)) // 顯示 'l'
println(str(4)) // 顯示 'a'

這並不是什麼神奇的功能,事實上當你操作字串時,呼叫了某個java.lang.String上所沒有提供的方法時,Scala會使用隱式轉換(Implicit conversion)將字串使用 scala.runtime.RichString 包裹起來,上面幾個例子,實際上都是操作RichString上的某些方法才得以完成(其中一些關於產生器、foreach、apply方法等,之後還會詳述)。

在字串中若要表示特定字元,例如"號,則要使用\來跳脫(Escape),例如:
val str = "This is \"Scala\"!"
println(str) // 顯示 This is "Scala" !

以下是跳脫字元的指定方式:
\\ 反斜線
\' 單引號 '
\" 雙引號 "
\uxxxx 以16 進位數指定Unicode字元輸出
\b 倒退一 個字元
\f 換頁
\n 換行
\r 游標移 至行首
\t 跳格 (一個Tab鍵)


在Scala中,可以使用"""來括住字串,稱之為原始字串(Raw string),上面的例子可以寫為以下形式:
val str = """This is "Scala"!"""
println(str) // 顯示 This is "Scala" !

在原始字串中,你不用跳脫字元,所以可以直接在原始字串中寫下"、\符號,不用特別跳脫"、\符號,對於撰寫正則表示式(Regular expression)時的簡潔很有幫助,例如原先撰寫以下的正則表示式:
val re = "\\d\\d\\d"
println("123-134".replaceAll(re, "###")) // 顯示 ###-###

因為正則表示式\d要寫在字串中,必須跳脫\符號,所以必須寫成\\d。如果使用Scala的原始字串,則只要寫成:
val re = """\d\d\d"""
println("123-134".replaceAll(re, "###")) // 顯示 ###-###

原始字串也可以跨越多行:
val text = """在原始字串中,你不用跳脫字元,
所以可以直接在原始字串中寫下"
、\符號,不用特別跳脫"、\符號..."""
println(text)

上面的程式看似只是縮排,不過要注意,在跨越多行時,空白也會被列為字串的一部份,所以程式執行後會顯示:
在原始字串中,你不用跳脫字元,
              所以可以直接在原始字串中寫下"
              、\符號,不用特別跳脫"、\符號...


如果不想連同空白也被列入,則可以使用 | 符號加上stripMargin方法,例如:
val text = """在原始字串中,你不用跳脫字元,
|所以可以直接在原始字串中寫下"
|、\符號,不用特別跳脫"、\符號...""".stripMargin
println(text)

程式執行後會顯示:
在原始字串中,你不用跳脫字元,
所以可以直接在原始字串中寫下"
、\符號,不用特別跳脫"、\符號...


如果你熟悉Java,對於字串的特性應該知道,直接在""中寫下字串,只要字元內容相同,無論出現幾次,在JVM中只會有一個字串實例,而==是用於比較兩個物件參考是否相同,所以下面這個Java程式結果是熟悉Java的程式設計人員都知道的事:
String str1 = "Java";
String str2 = "Java"
String str3 = new String(str1);
System.out.println(str1 == str2); // 顯示 true
System.out.println(str1 == str3); // 顯示 false
System.out.println(str1.equals(str3)); // 顯示 true

先前說過,在Scala中重用了java.lang.String,也許你會認為以下的程式也會顯示true、false、true,不過結果可能會令你訝異:
val str1 = "Scala"
val str2 = "Scala"
val str3 = new String(str1)
println(str1 == str2) // 顯示 true
println(str1 == str3) // 顯示 true
println(str1.equals(str3)) // 顯示 true

這 是因為==在Scala中,是比較物件實質內容是否相同,這點與Java並不同,也因此第二個顯示會是true,在Scala中基本上也沒有必要直接呼叫 equals方法,而可以直接使用==取代(之後會介紹,在Scala中,若要定義物件實質相等性,也是重新定義equals()方法,因為==會呼叫 equals()方法)。

如果你真的要比較兩個物件參考是否相同,可以使用eq或ne方法,例如:
val str1 = "Scala"
val str2 = "Scala"
val str3 = new String(str1)
println(str1 eq str2) // 顯示 true
println(str1 eq str3) // 顯示 false
println(str1 == str3) // 顯示 true