1. 程式人生 > >Scala基礎教程(八):模式匹配、正則表示式

Scala基礎教程(八):模式匹配、正則表示式

匹配使用case 類:

case classes是用於模式匹配與case 表示式指定類。這些都是標準類具有特殊修飾:case。下面是一個簡單的模式使用case class匹配示例:

object Test {
   def main(args: Array[String]) {
          val alice = new Person("Alice", 25)
             val bob = new Person("Bob", 32)
          val charlie = new Person("Charlie", 32)
      for (person <-
List(alice, bob, charlie)) {
         person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) =>
               println("Age: " + age + " year, name: " + name + "?")
         }
      }
   }
   // case class, empty one.
   case class Person(name: String, age: Int)
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?
C:/>

增加 case 關鍵字使編譯器自動添加了許多實用的功能。關鍵字建議與模式匹配的情況下表達式的關聯。

首先,編譯器會自動轉換的建構函式的引數為不可變的欄位(vals)。val關鍵字是可選的。如果想可變欄位,使用var關鍵字。因此,建構函式的引數列表現在更短。

其次,編譯器自動實現equals, hashCode, 和toString方法的類,它使用指定為建構函式引數的欄位。因此,不再需要自己的toString方法。

最後,還消失Person類的主體部分,因為沒有需要定義的方法!

Scala支援通過Regex類的scala.util.matching封裝正則表示式。讓我們看看一個例子,我們將嘗試從Scala中一個語句中找出單詞:

import scala.util.matching.Regex
object Test {
   def main(args: Array[String]) {
      val pattern = "Scala".r
      val str = "Scala is Scalable and cool"
      println(pattern findFirstIn str)
   }
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
Some(Scala)
C:/>

我們建立一個字串,並呼叫r()方法就可以了。Scala中字串隱式轉換為一個RichString並呼叫該方法來獲得正則表示式的一個例項。找到第 一個正則表示式匹配,只需呼叫findFirstIn()方法。而非只找到第一次出現。如果想找到匹配的單詞的所有事件,可以使用findAllIn() 方法,並在情況下,有目標字串中使用多個Scala的單詞,這將返回所有匹配的集合單詞。

可以使用mkString()方法來連線所產生的服務,可以使用管道(|)搜尋Scala中小型和資本的情況下,使用正則表示式構造來代替或r()方法建立一個模式如下:

import scala.util.matching.Regex
object Test {
   def main(args: Array[String]) {
      val pattern = new Regex("(S|s)cala")
      val str = "Scala is scalable and cool"
      println((pattern findAllIn str).mkString(","))
   }
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
Scala,scala
C:/>

如果想更換匹配的文字,可以使用replaceFirstIn()以取代第一個匹配項或replaceAllIn(),以取代所有出現如下:

object Test {
   def main(args: Array[String]) {
      val pattern = "(S|s)cala".r
      val str = "Scala is scalable and cool"
      println(pattern replaceFirstIn(str, "Java"))
   }
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
Java is scalable and cool
C:/>

形成正則表示式:

Scala繼承了Java,這反過來又繼承了大部分的Perl的功能,它的正則表示式語法。這裡只是一些例子,應該是足夠的說明:

下面是表,列出了所有的正則表示式元字元的語法可用在Java中:

子表示式

匹配

^

匹配行頭

$

匹配行尾

.

匹配除換行符任何單個字元。用m選項也允許使之匹配換行符。

[...]

匹配括號內任何單個字元。

[^...]

匹配任何單個字元不是在括號中

\A

整個字串的開始

\z

整個字串結束

\Z

最終,除了允許的最後行結束整個字串。

re*

匹配0或多次出現前面表示式。

re+

匹配1個或多個的先前東西

re?

匹配01發生前表示式。

re{ n}

精確匹配n個前面表示式的數量。

re{ n,}

匹配n或多次出現前面的表達。

re{ n, m}

至少匹配n和在前面的表現最為m次出現。

a|b

匹配ab

(re)

組正則表示式並記住匹配的文字。

(?: re)

組正則表示式而不記住匹配的文字。

(?> re)

匹配獨立模式而不反向追蹤。

\w

匹配單詞字元。

\W

匹配非單詞字元。

\s

匹配空白。相當於 [ f].

\S

匹配非空白。

\d

匹配數字。相當於 [0-9].

\D

匹配非數字。

\A

匹配開始的字串。

\Z

匹配字串的結尾。如果一個換行符存在,它只是換行之前匹配。

\z

匹配字串的結尾。

\G

匹配點,最後一次匹配結束。

\n

反向引用以捕獲組編號 "n"

\b

匹配單詞邊界之外時,括號內。匹配退格(0×08)括號裡面。

\B

匹配非單詞邊界。

\n, \t, etc.

匹配換行符,回車,製表符等

\Q

轉義(引用)所有字元為 \E

\E

尾部引用開始 \Q

正則表示式的例子:

示例

描述

.

匹配除了換行符的任何字元

[Rr]uby

匹配 "Ruby" "ruby"

rub[ye]

匹配"ruby"  "rube"

[aeiou]

匹配任何一個小寫母音

[0-9]

匹配任何數字; [0123456789]

[a-z]

匹配任意小寫ASCII字母

[A-Z]

匹配任意大寫ASCII字母

[a-zA-Z0-9]

匹配任何上述

[^aeiou]

匹配母音以外的任何一個小寫字元

[^0-9]

匹配數字以外的任何其他

\d

匹配一個數字: [0-9]

\D

匹配一個非數字: [^0-9]

\s

匹配一個空白字元: [ f]

\S

匹配非空白: [^ f]

\w

匹配一個字元: [A-Za-z0-9_]

\W

匹配一個非單詞字元: [^A-Za-z0-9_]

ruby?

匹配 "rub" or "ruby": the y is optional

ruby*

匹配 "rub" plus 0 or more ys

ruby+

匹配 "rub" plus 1 or more ys

\d{3}

匹配只有 3 個數字

\d{3,}

匹配 3 個或多個數字

\d{3,5}

匹配3, 4, 5 個數字

\D\d+

不分組: + repeats \d

(\D\d)+/

分組: + repeats \Dd

([Rr]uby(, )?)+

匹配 "Ruby", "Ruby, ruby, ruby", .

需要注意的是每一個反斜槓上述字串中出現兩次。這是因為在Java和Scala一個反斜槓是一個轉義字元的字串,而不是一個普通字元顯示出來的字串。所以不是.. 需要寫.\ 。得到的字串中的一個反斜槓。請檢視下面的例子:

import scala.util.matching.Regex
object Test {
   def main(args: Array[String]) {
      val pattern = new Regex("abl[ae]\d+")
      val str = "ablaw is able1 and cool"
      println((pattern findAllIn str).mkString(","))
   }
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
able1
C:/>

Scala的異常的工作像許多其他語言,如Java異常。而不是正常方式返回的值,方法可以通過丟擲一個異常終止。然而,Scala實際上並沒有檢查異常。

當要處理異常,那麼可使用try{...}catch{...} 塊,就像在Java中除了catch塊採用匹配識別和處理異常。

丟擲異常:

丟擲一個異常看起來類似於Java。建立一個異常物件,然後使用throw關鍵字把它丟擲:

throw new IllegalArgumentException

捕獲異常:

Scala中try/catch在一個單獨的塊捕捉任何異常,然後使用case塊進行模式匹配,如下圖所示:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException =>{
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      }
   }
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
Missing file exception
C:/>

這種try-catch表達的行為在其他語言處理異常是一樣的。body是執行體,如果它丟擲一個異常,每個catch子句都依次嘗試捕獲。

finally子句:

如果想知道引起一些程式碼是如何表達的終止執行,可以用一個finally子句包住一個表示式,finally塊什麼時候都會執行。

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException => {
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      } finally {
         println("Exiting finally...")
      }
   }
}

當上述程式碼被編譯和執行時,它產生了以下結果:

C:/>scalac Test.scala
C:/>scala Test
Missing file exception
Exiting finally...
C:/>

提取器在Scala中是一個物件,有一個叫非應用作為其成員的一種方法。即不應用方法的目的是要匹配的值,並把它拆開。通常,提取物件還限定了雙方法申請構建值,但是這不是必需的。

下面的例子顯示電子郵件地址的提取器物件:

object Test {
   def main(args: Array[String]) {
      println ("Apply method : " + apply("Zara", "gmail.com"));
      println ("Unapply method : " + unapply("[email protected]"));
      println ("Unapply method : " + unapply("Zara Ali"));
   }
   // The injection method (optional)
   def apply(user: String, domain: String) = {
      user +"@"+ domain
   }
   // The extraction method (mandatory)
   def unapply(str: String): Option[(String, String)] = {
      val parts = str split "@"
      if (parts.length == 2){
         Some(parts(0), parts(1)) 
      }else{
         None
      }
   }
}

這個物件定義了 apply 和unapply 方法。該apply 方法具有相同的含義:它原來的測試為可以被應用到的引數在括號中的方法所應用的相同的方式的物件。所以,可以寫為Test("Zara", "gmail.com") 來構造字串“[email protected]”。

unapply方法使測試類成為一個提取器並反轉應用的構造過程。應用需要兩個字串,並形成了一個電子郵件地址以找到它們,非應用unapply需要一個電子郵件地址,並可能返回兩個字串:使用者和地址的域名。

unapply還必須處理中給定的字串不是一個電子郵件地址的情況。這就是為什麼unapply返回一個選項型過對字串。其結果要麼是一些(使用者域) 如果字串str使用給定電子郵件地址的使用者和域的部分,或None,如果str不是一個電子郵件地址。下面是一些例子:

unapply("[email protected]") equals Some("Zara", "gmail.com")
unapply("Zara Ali") equals None

讓我們編譯和執行上面的程式,這將產生以下結果:

C:/>scalac Test.scala
C:/>scala Test
Apply method : Zara@gmail.com
Unapply method : Some((Zara,gmail.com))
Unapply method : None
C:/>

from: http://www.yiibai.com/scala/scala_basic_syntax.html