Scala之String

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編程中使用頻率很是高的知識點,對其進行了梳理,加深了印象,也謝謝各位園友的觀看~ 

相關文章
相關標籤/搜索