1、前言正則表達式
大數據領域的Spark、Kafka、Summingbird等都是由Scala語言編寫而成,相比Java而言,Scala更精煉。因爲筆者從事大數據相關的工做,因此有必要好好學習Scala語言,以前也學習過,可是沒有記錄,因此就會忘記,感受Scala確實比Java方便精煉不少,下面以Scala Cookbook英文版做爲參考資料,從頭至尾梳理Scala相關知識點,也加深印象。PS:這是在研究Zookeeper源碼的間隙中交叉學習,不至於老是看源碼太枯燥。編程
2、Stringapi
在Scala的REPL環境中測試String的類型,能夠發現其就是Java中的String。數組
因此可使用Java中String的全部方法,如獲取字符串的長度、鏈接多個字符串。在Scala中,因爲String能夠被隱式轉化成StringOps類型,可將字符串當作一個字符序列,而且可使用foreach方法遍歷字符串的每一個字符。app
也可將字符串當成字符序列,使用for循環遍歷每一個字符編程語言
同理,也可將字符串當前字節序列,使用for循環遍歷函數
因爲字符串能夠當成字符順序集合(字符序列),而在集合上可進行多種操做,如filter工具
能夠看到對字符串進行了過濾,去掉了l字符。filter方法是StringOps的方法,因爲String會被隱式的轉化爲StringOps,所以能夠調用此方法。學習
對於該隱式轉化,是在Perdef對象中定義。 測試
@inline implicit def augmentString(x: String): StringOps = new StringOps(x)
向封閉類中添加方法
在Java中,String被定義成final的,即沒法繼承String類或者添加任何方法,但在Scala中,咱們能夠經過隱式轉化添加方法,下面例子展現了Scala的String擁有String的特性和集合的特性。
其中,drop方法和take方法都是Scala的序列(集合)的方法,而capitalize方法則是StringOps的方法,這種調用都是經過隱式轉化完成的。
2.1 測試String的相等性
1. 問題描述
你須要比較兩個字符串以判斷它們是否相等,或者它們包含相同的字符序列。
2. 解決方案
定義了以下字符串s一、s二、s3
使用"=="進行相等性判斷
能夠看到s一、s二、s3均相等,而對於一個好的"=="方法而言,即使是有參數爲空,也不會拋出異常。
若在比較過程當中不區分大小寫,則可將字符串轉化成大寫或小寫進行比較。
然而,在對空字符串調用toUpperCase方法會拋出異常。
在Java中,想要比較不區分大小寫比較兩個字符串是否相等,則使用equalsIgnoreCase方法
3. 討論
在Scala中,咱們使用==方法來斷定對象的相等性,這與Java不一樣,Java使用equals方法斷定兩個對象的相等性,== 是比較兩個對象是不是同一個對象(內存地址相同)。在Scala中,== 方法在AnyRef(全部引用類型的父類)中定義,其首先會檢查空值,而後再調用第一個對象的equals方法進行比較,所以,當比較兩個字符串是否相等時,咱們不須要檢查空值。
2.2 建立多行字符串
1. 問題描述
在Scala源碼中想要建立多行字符串。
2. 解決方案
在Scala中,你可使用三個雙引號來建立多行字符串。
val s1 = """This is a multiline String """ println(s1)
3. 討論
使用上述方法能夠建立多行字符串,可是當打印時,其結果以下
This is a multiline String
第二行和第三行是以空格開頭,若須要使第二行、第三行的字符串都不以空格開頭,則可進行以下處理。
val s1 = """ |This |is a multiline |String """.stripMargin println(s1)
結果以下
This is a multiline String
即經過|來處理,或者使用其餘符號,如#,同時使用stripMargin方法。
val s1 = """This is #a multiline #String """.stripMargin('#') println(s1)
結果以下
This is a multiline String
上述例子中,在第一行is和第二行multiline後面均隱藏了\n字符進行換行,當須要將多行字符串合併爲一行時,能夠在使用stripMargin方法以後使用replaceAll方法來將全部的\n替換爲" "。
另外,在三引號字符串中能夠包含特殊字符,而並不須要轉義符進行轉義。
2.3 分割字符串
1. 問題描述
你須要使用分割字符來分割字符串,如從逗號分隔(CSV)或管道分隔的文件中獲取字符串。
2. 解決方案
可使用String的split方法進行分割
split方法會返回String數組。
3. 討論
split函數的參數能夠是正則表達式,因此對於CSV文件,你可使用逗號進行分割字符串。
能夠看到,經過","進行分割時,結果中還包含了一些空格,如" milk", " butter", " Coco Puffs",此時,須要使用trim函數來去掉空格。
咱們也可使用正則表達式來分割字符串
split方法是重載的,一部分從Java的String而來,一部分從Scala的StringLike而來,例如,你可使用字符而非字符串做爲參數來調用split,此時,你使用的是StringLike的方法
此時,使用字符和字符串做爲參數二者的結果是相同的。
2.4 將變量替換成字符串
1. 問題描述
如同Perl、PHP、Ruby同樣,你須要將變量替換成字符串。
2. 解決方案
爲在Scala中使用字符串插值,字符串前面須要使用字母s,同時須要將變量包含在字符串中,而每一個變量名前面是一個$字符。
當在字符串前面使用字母s時,表示正在建立一個已處理的字符串字面量,便可以在字符串中直接使用變量。
字符串字面量中使用表達式
除了在字符串中使用變量外,一樣也能夠在字符串中使用表達式,此時表達式須要放在中括號內。
也可使用中括號打印對象的屬性。
s是一個方法
放在字符串字面量前面的s其實是一個方法,而使用s方法可讓你享受以下便利
· Scala提供了其餘現成的插值函數
· 你可自定義字符串插值函數
f字符串插值(printf格式)
在討論中提到,weight被打印成65,可是若是須要在weight後面增長多位小數點應該怎麼作,可使用f字符串插值方法,其能夠格式化字符串中的說明符。
爲使用f字符串插值,首先須要在字符串前面添加f,而後在變量後面使用printf格式化說明符。
粗插值
除了使用s和f插值方法外,Scala還包括了粗插值方法,其會保留字符串中的特殊字符。
能夠看到使用raw修飾字符串時,其會保留字符串中的特殊字符。
下表列出了最經常使用的說明符
2.5 一次處理字符串的一個字符
1. 問題描述
你須要遍歷字符串的每一個字符,而且對每一個字符作相應的操做。
2. 解決方案
可使用map方法、foreach方法、for循環等方法來遍歷字符串。
或者使用下劃線的方式
對於字符串的字符序列,你可使用鏈式調用來獲得想要的結果,在下面的示例中,filter方法用於原始的字符串來生成新的字符串(去掉全部的字符l),而後再調用map方法將新生成的字符串轉化爲大寫。
使用for循環和yield也能夠達到map方法的效果
map方法、for和yield方法能夠將舊的集合轉化爲新的集合,而foreach方法則是對集合的每一個元素進行操做,不會產生新的結果。
3. 討論
因爲Scala將字符串當成是字符序列,而Scala也是面向對象和函數編程語言,在Java中,你可使用以下方法來遍歷字符串中的每一個字符
String s = "hello" for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); System.out.println(c); }
理解map方法的工做機制
在map方法中,你能夠傳入一大段代碼塊
上述功能是將字符串的字符由大寫變換成小寫,因爲是調用的String的map方法,所以每次只會處理字符串的一個字符,map會將String當成一個字符順序集合,map方法有一個隱式循環,在此循環中,每次只會傳入一個字符。除了在map方法中直接傳入代碼塊外,也能夠先定義好函數,而後再傳入map,這樣能夠保證代碼的簡潔性。
而且也能夠在for循環和yield中使用該方法
除了使用方法方式外,還可使用函數方式來完成上述的操做
2.6 在字符串中查找模式
1. 問題描述
你須要肯定字符串是否包含正則表達式模式。
2. 解決方案
經過String的.r方法來建立一個Regex對象,以後當查找第一個匹配時,使用findFirstIn方法,而查找全部匹配時,使用findAllIn方法。
對於findAllIn方法而言,能夠將結果轉化成Array、List、Seq等
3. 討論
使用字符串的.r方法是建立Regex對象最簡單的方式,另一種方式是導入Regex類,建立一個Regex實例,以後使用實例的方法
處理findFirstIn返回的結果
findFirstIn查找第一個匹配而且返回一個Option[String]
Option/Some/None類型將會在以後的章節進行討論,能夠簡單的認爲Option是一個容器,它要麼持有0或者一個值,對於findFirstIn,成功時會返回Some("123"),不成功時會返回None
返回類型爲Option[String]的方法要麼返回Some(String),要麼返回None
針對Option類型,想要獲取其值,可使用以下方法
· getOrElse
· 使用foreach
· 使用匹配表達式
使用getOrElse方法,你能夠嘗試獲取值,或者失敗時定義缺省值
使用foreach方法以下
使用匹配表達式方法以下
2.7 字符串的替換模式
1. 問題描述
你須要要在字符串中搜索正則表達式模式,並替換它們。
2. 解決方案
因爲String是不可變的,因此你不能直接在字符串上進行查找並替換的操做,可是你能夠建立包含替換內容的新字符串,可使用replaceAll方法,要記得將結果賦值
也可建立一個正則表達式,而後調用replaceAllIn方法,一樣要記得將結果賦值給新的字符串
也能夠調用replaceFirstIn來替換第一個匹配值,一樣要記得將結果賦值給新的字符串
2.8 提取模式匹配的字符串部分
1. 問題描述
你須要提取字符串的一個或多個匹配正則達表達式的部分。
2. 解決方案
首先定義提取的正則表達式模式,而後將它們放置在括號裏面造成正則表達式組
上述示例從指定字符串中提取了數字部分和字母部分,而且分別賦值給count和fruit。
3. 討論
上述示例的語法可能有點古怪,彷佛是將模式兩次定義成val字段,可是這種語法很便捷而且可讀性很高,能夠試想你在編寫一個搜索引擎,你想讓人們用各類各樣的短語搜索電影,你可讓他們輸入任何這些短語來得到電影列表
你能夠定義一系列的正則表達式進行匹配,例如
// match "movies 80301" val MoviesZipRE = "movies (\\d{5})".r // match "movies near boulder, co" val MoviesNearCityStateRE = "movies near ([a-z]+), ([a-z]{2})".r
以後能夠根據用戶的輸入進行匹配,而後獲取搜索結果,僞代碼以下
textUserTyped match { case MoviesZipRE(zip) => getSearchResults(zip) case MoviesNearCityStateRE(city, state) => getSearchResults(city, state) case _ => println("did not match a regex") }
上述的正則表達式能夠匹配以下字符串
在匹配時,須要考慮全部狀況,如case _ 表示不能匹配,以下字符串將沒法匹配
2.9 訪問字符串的字符
1. 問題描述
你想要獲取字符串中特定位置的字符。
2. 解決方案
可使用Java的charAt方法
另外,一種更好的方法是數組表示法
3. 討論
當map方法和foreach不適用時,能夠將String看作Array類型,而後使用數組表示法來訪問字符,Scala中的數組表示法不一樣於Java的數組表示法,由於在Scala中,數組表示法是一個方法調用。
在調用數組表示法時實際上調用的是apply方法,,可是因爲Scala語法糖的存在,能夠直接使用數組表示法獲取指定字符。
2.10 向String類添加自定義方法
1. 問題描述
你想要向字符串類添加自定義方法,如"HAL".increment,而非使用工具類StringUtilities的increment方法。
2. 解決方案
能夠定義一個隱式類,而後在類中定義你想要添加的方法
在實際的編碼中會稍微複雜一點,由於隱式類須要必需要被定義在能夠被定義方法的做用域中,這意味着隱式類必須定義在class、object、package object中。
package com.leesf.utils object StringUtils { implicit class StringImprovements(val s: String) { def increment = s.map(c => (c + 1).toChar) } }
在使用時,須要導入com.leesf.utils.StringUtils
package foo.bar import com.leesf.utils.StringUtils._ object Main extends App { println("HAL".increment) }
還可將隱式類放在包對象中
package com.leesf package object utils { implicit class StringImprovements(val s: String) { def increment = s.map(c => (c + 1).toChar) } }
在使用時,須要導入com.leesf.utils
package foo.bar import com.leesf.utils._ object MainDriver extends App { println("HAL".increment) }
針對Scala2.10前的版本,與上述作法有稍許不一樣,首先須要在普通的類中定義increment方法
class StringImprovements(val s: String) { def increment = s.map(c => (c + 1).toChar) }
而後定義隱式方法進行轉化
implicit def stringToString(s: String) = new StringImprovements(s)
3. 討論
在Scala中,你能夠經過隱式轉化向封閉的類添加新的方法,而且在使用時導入,而不須要繼承該類來添加方法(有些final類根本不能繼承)。
3、總結
本篇博文講解了Scala中的String知識點,其也是在Scala編程中使用頻率很是高的知識點,對其進行了梳理,加深了印象,也謝謝各位園友的觀看~