第11章 使用正則表達式的模式匹配(二)

11.2 用於模式匹配的 String 方法

迄今爲止,雖然本章已經討論過了用於創建正則表達式的語法,但是我們還沒有檢驗過這些正則表達式在 JavaScript 代碼中如何使用。在這一節中,我們將討論 String 對象的部分方法,它們在正則表達式中執行模式匹配和檢索與替換操作。在此後的小節中,我們將繼續討論使用 JavaScript 正則表達式的模式匹配,不過討論的是 RegExp 對象和它的方法及屬性。注意,接下來的討論只是與正則表達式相關的各種方法和屬性的概述。

類 String 支持四種利用正則表達式的方法。

(1)最簡單的是 search() 。該方法以正則表達式爲參數,返回第一個與之匹配的子串的開始字符的位置,如果沒有任何匹配的子串,它將返回 -1 。例如,下面的調用返回的值爲 4:

"JavaScript".search(/script/i);

如果 search() 的參數不是正則表達式,它首先將被傳遞給 RegExp 構造函數,轉換成正則表達式。 search() 不支持全局檢索,因爲它忽略了正則表達式參數的標誌 g 。

(2)方法 replace() 執行檢索與替換操作。它的第一個參數是一個正則表達式,第二個參數是要進行替換的字符串。它將檢索調用它的字符串,根據指定的模式來匹配。如果正則表達式中設置了標誌 g ,該方法將用替換字符串替換被檢索的字符串中所有與模式匹配的子串,否則它只替換所發現的第一個與模式匹配的子串。如果 replace() 的一個參數是字符串,而不是正則表達式,該方法將直接檢索那個字符串,而不是像 search() 那樣用 RegExp() 構造函數將它轉換成一個正則表達式。例如,我們可以用如下方法使用 replace() 將文本字符串中的所有 javascript (不區分大小寫) 統一爲 "JavaScript" :

text.replace(/javascript/gi, "JavaScript");

但是 replace() 的功能遠比上例所示強大。回憶一下可以知道,正則表達式中用括號括起來的子表達式是從左到右進行編號的,而且正則表達式會記住與每個子表達式匹配的文本。如果在替換字符串中出現了符號 $ 加數字,那麼 replace() 將用與指定的子表達式相匹配的文本來替換這兩個字符這是一個非常有用的特性。例如,我們可以用它將一個字符串中的直接引號替換爲大引號,這與 ASCII 字符相似:




最值得注意的是,replace() 方法的第二個參數可以是函數,該函數能夠動態地計算替換字符串。

(3)方法 match() 是最常用的 String 正則表達式方法。它唯一的參數就是一個正則表達式 (或把它的參數傳遞給構造函數 RegExp() 以轉換成正則表達式),返回的是包含了匹配結果的數組。如果該正則表達式設置了標誌 g ,該方法返回的數組包含的就是出現在字符串中的所有匹配。例如:

”1 plus 2 equals 3".match(/\d+/g)                 // 返回 ["1", "2", "3"]

如果該正則表達式沒有設置標誌 g ,match() 進行的就不是全局性檢索,它只是檢索第一個匹配。但即使 match() 執行的不是全局檢索,它也返回一個數組。在這種情況下,數組的第一個元素就是匹配的字符串,而餘下的元素則是正則表達式中用括號括起來的子表達式。因此,如果 match() 返回了一個數組 a ,那麼 a[0] 存放的是完整的匹配,a[1] 存放的則是與第一個用括號括起來的表達式匹配的子串,以此類推。爲了和方法 replace() 保持一致, a[n] 存放的是 $n 的內容。

例如,使用如下的代碼來解析一個 URL :


最後,還應該瞭解 match() 的一個特性。它返回的數組和其他的數組一樣具有一個 length 屬性。如果 match() 是作用於一個非全局的正則表達式,那麼它返回的數組還包含另外兩個屬性 -- index 和 input ,前者包含的是在字符串中匹配開始處的字符的位置,後者則是目標字符串的一個副本。這樣,在上面的代碼中,result.index 屬性的值應該等於 17 ,因爲匹配了的 URL 是從文本中的第 17 個字符的位置開始。而屬性 result.input 則應該具有和變量 text 相同的字符串。對於沒有標誌  g 的正則表達式 r 和字符串 s ,調用 s.match(r) 返回的值與 r.exec(s) 相同。

(4)String 對象的最後一個有關正則表達式的方法是 split() 。這個方法可以把調用它的字符串分解爲一個子串數組,使用的分隔符是它的參數。例如:

"123,456,789".split(",") ;                    // Returns ["123", "456", "789"]

split() 方法也可以以正則表達式爲參數。這種能力使該方法更強大。例如,我們可以指定分隔符,允許兩邊有任意多個空白符:

"1,2,  3 ,     4    ,5".split(/\s*,\s*/);        // Returns["1","2","3","4","5"]

11.3 RegExp 對象

正則表達式使用 RegExp 對象來表示的。除了構造函數 RegExp() 之外,RegExp 對象還支持三種方法和大量的屬性。RegExp 類的一個不尋常的特性是它既定義了類 (靜態) 屬性,又定義了實例屬性。也就是說,它既定義了屬於構造函數 RegExp() 的全局屬性,又定義了其他屬於單獨的 RegExp 對象的屬性。我們將在接下來的兩節中介紹 RegExp 的模式匹配方法和屬性。

構造函數 RegExp() 有一個或兩個字符串參數,它將創建一個新的 RegExp 對象。該構造函數的第一個參數是包含正則表達式主體的字符串,即正則表達式直接量中出現在斜線對之間的文本。注意,無論是字符串直接量還是正則表達式都是用了字符 \ 表示轉義序列,所以當將正則表達式作爲字符串直接傳遞給 RegExp() 時,必須用 \\ 替換所有 \ 字符。RegExp() 的第二個參數是可選的。如果提供了這個參數,它說明的就是該正則表達式的標誌。它應該是 "g" 、"i"、 "m" 或它們的組合。例如:

var zipcode = new RegExp("\\d{5}", "g");

當要動態創建一個正則表達式,而不能用正則表達式直接量的語法來表示時,構造函數 RegExp() 非常有用。例如,如果檢索的字符串是由用戶輸入的,那麼就必須在運行時用 RegExp() 構造函數來創建正則表達式。

11.3.1 用於模式匹配的 RegExp 方法

RegExp() 對象定義了兩個用於執行模式匹配操作的方法。它們的行爲和前面介紹過的 String 方法很相似。主要的 RegExp 模式匹配方法是 exec() 。它與之前介紹過的 String 方法 match() 相似,只不過它是以字符串爲參數的 RegExp 方法,而不是以  RegExp 對象爲參數的 String 方法。exec() 方法對一個指定的字符串執行一個正則表達式,簡而言之,就是在一個字符串中檢索匹配。如果沒有找到任何匹配,它將返回  null ,但是,如果它找到了一個匹配,將返回一個數組,就像方法 match() 爲非全局檢索返回的數組一樣。這個數組的元素 0 包含的是與正則表達式相匹配的字符串,餘下的所有元素包含的是與括號括起來的子表達式相匹配的子串。而且,屬性 index 包含了匹配發生的字符的位置,屬性 input 引用的是被檢索的字符串。

和 match() 方法不同的是,exec() 返回的數組類型相同,無論該正則表達式是否具有全局標記 g 。回憶一下,當傳遞給方法 match() 的是一個全局正則表達式時,它返回的是一個匹配的數組。相比之下,方法 exec() 返回的總是一個匹配,而且提供關於該匹配的完整信息。當一個具有 g 標誌的正則表達式調用 exec() 時,它將把該對象的 lastIndex 屬性設置到緊接着匹配子串的字符位置。當一個正則表達式第二次調用 exec() 時,它將從 lastIndex 屬性所指示的字符處開始檢索。如果 exec() 沒有發現任何匹配,它會將 lastIndex 屬性重置爲 0 (在任何時候都可以將 lastIndex 屬性設爲 0 ,每當在一個字符串中找到最後一個匹配並開始用同一個 RegExp 對象來檢索另一個字符串時,退出本次檢索應該這樣做)。這一特殊的行爲使得可以反覆調用 exec() 遍歷一個字符串中所有匹配的正則表達式。例如:

var pattern = /Java/g;

var text = "JavaScript is more fun than Java!";

var result;

while((result = pattern.exec(text)) != null){

alert("Matched '" + result[0] + "'" + " at  position " +result.index + "; next search begins at " + pattern.lastIndex);

}

另一個 RegExp 方法是 test() ,它比 exec() 方法簡單一些。它的參數是一個字符串,如果這個字符串包含正則表達式的一個匹配,它就返回 true :

var pattern = /java/;

pattern.test("JavaScript");         // Returns true

調用 test() 方法等價於調用 exec() 方法,如果 exec() 的返回值不是 null,它將返回 true 。由於這種等價性,當一個全局正則表達式調用方法 test() 時,它的行爲和方法 exec() 相同,即它從 lastIndex 指定的位置處開始檢索特定的字符串,如果它發現了匹配,就將 lastIndex 設置爲緊接在那個匹配之後的字符的位置。這樣一來,我們就可以使用方法 test() 來遍歷字符串,就像用 exec() 方法那樣。

String 方法 search()、replace() 和 match() 都沒有像 exec() 和 test() 那樣使用屬性 lastIndex 。事實上,String 方法只是將 lastIndex() 重置爲 0 。如果使用一個設置了 g 標記模式的 exec() 方法或 test() 方法來檢索多個字符串,那麼就必須找到每個字符串中的所有匹配,以便 lastIndex 屬性會被自動重置爲 0 (這在最後的檢索失敗時會發生),或者必須明確地將 lastIndex 屬性設爲 0 。如果忘記了這樣做,那麼再檢索一個新字符串時,起始位置可能就是原來的字符串中的任意位置,而不一定是開頭。最後要記住,只有帶 g 標誌的正則表達式纔會發生這種特殊的 lastIndex 行爲。如果 RegExp 對象沒有標誌 g ,exec() 和 test() 將忽略它的 lastIndex 屬性。

11.3.2 RegExp 的實例屬性

每個 RegExp 對象都有五個屬性。屬性 source 是一個只讀字符串,它存放的是正則表達式的文本。屬性 global 是一個只讀的布爾值,它說明了該正則表達式是否具有標誌 g 。屬性 ignoreCase 也是一個只讀的布爾值,它說明了該正則表達式是否具有標誌 i 。屬性 multiline 是一個只讀的布爾值,它說明了正則表達式是否具有標誌 m 。最後一個屬性是 lastIndex ,它是一個可讀寫的整數。對於具有標誌 g 的模式,這個屬性存儲在字符串中下一次開始檢索的位置。它由方法 exec() 和 test() 使用,我們在上一節中已經介紹過。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章