選擇允許使用 '|' 字符來在兩個(gè)或多個(gè)候選項(xiàng)中進(jìn)行選擇。通過擴(kuò)展章節(jié)標(biāo)題的正則表達(dá)式,可以將其擴(kuò)充為不僅僅適用于章節(jié)標(biāo)題的表達(dá)式。不過,這可沒有想象的那么直接。在使用選擇時(shí),將匹配'|' 字符每邊最可能的表達(dá)式。你可能認(rèn)為下面的 JScript 和 VBScript 表達(dá)式將匹配位于一行的開始和結(jié)束位置且后跟一個(gè)或兩個(gè)數(shù)字的 'Chapter' 或 'Section':
/^Chapter|Section [1-9][0-9]{0,1}$/ "^Chapter|Section [1-9][0-9]{0,1}$"
不幸的是,真正的情況是上面所示的正則表達(dá)式要么匹配位于一行開始處的單詞 'Chapter',要么匹配一行結(jié)束處的后跟任何數(shù)字的 'Section'。如果輸入字符串為 'Chapter 22',上面的表達(dá)式將只匹配單詞 'Chapter'。如果輸入字符串為 'Section 22',則該表達(dá)式將匹配 'Section 22'。但這種結(jié)果不是我們此處的目的,因此必須有一種辦法來使正則表達(dá)式對于所要做的更易于響應(yīng),而且確實(shí)也有這種方法。
可以使用圓括號來限制選擇的范圍,也就是說明確該選擇只適用于這兩個(gè)單詞 'Chapter' 和 'Section'。不過,圓括號同樣也是難處理的,因?yàn)樗鼈円灿脕韯?chuàng)建子表達(dá)式,有些內(nèi)容將在后面關(guān)于子表達(dá)式的部分介紹。通過采用上面所示的正則表達(dá)式并在適當(dāng)位置添加圓括號,就可以使該正則表達(dá)式既可以匹配 'Chapter 1',也可以匹配 'Section 3'。
下面的正則表達(dá)式使用圓括號將 'Chapter' 和 'Section' 組成一組,所以該表達(dá)式才能正確工作。對 JScript 為:
/^(Chapter|Section) [1-9][0-9]{0,1}$/
對 VBScript 為:
"^(Chapter|Section) [1-9][0-9]{0,1}$"
這些表達(dá)式工作正確,只是產(chǎn)生了一個(gè)有趣的副產(chǎn)品。在 'Chapter|Section' 兩邊放置圓括號建立了適當(dāng)?shù)木幗M,但也導(dǎo)致兩個(gè)待匹配單詞之一都被捕獲供今后使用。由于在上面所示的表達(dá)式中只有一組圓括號,因此只能有一個(gè)捕獲的 submatch??梢允褂?VBScript 的Submatches 集合或者JScript 中RegExp 對象的 $1-$9 屬性來引用這個(gè)子匹配。
有時(shí)捕獲一個(gè)子匹配是所希望的,有時(shí)則是不希望的。在說明所示的示例中,真正想做的就是使用圓括號對單詞 'Chapter' 或 'Section' 之間的選擇編組。并不希望在后面再引用該匹配。實(shí)際上,除非真的是需要捕獲子匹配,否則請不要使用。由于不需要花時(shí)間和內(nèi)存來存儲(chǔ)那些子匹配,這種正則表達(dá)式的效率將更高。
可以在正則表達(dá)式模式圓括號內(nèi)部的前面使用 '?:'來防止存儲(chǔ)該匹配供今后使用。對上面所示正則表達(dá)式的下述修改提供了免除子匹配存儲(chǔ)的相同功能。對 JScript:
/^(?:Chapter|Section) [1-9][0-9]{0,1}$/
對 VBScript:
"^(?:Chapter|Section) [1-9][0-9]{0,1}$"
除了 '?:' 元字符,還有兩個(gè)非捕獲元字符用于稱之為預(yù)查的匹配。一個(gè)為正向預(yù)查,用 ?= 表示, 在任何開始匹配圓括號內(nèi)的正則表達(dá)式模式的位置來匹配搜索字符串。一個(gè)為負(fù)向預(yù)查,用 '?!' 表示,在任何開始不匹配該正則表達(dá)式模式的位置來匹配搜索字符串。
例如,假定有一個(gè)包含引用有 Windows 3.1、Windows 95、Windows 98 以及 Windows NT 的文檔。進(jìn)一步假設(shè)需要更新該文檔,方法是查找所有對 Windows 95、Windows 98 以及 Windows NT 的引用,并將這些引用更改為 Windows 2000。可以使用下面的 JScript 正則表達(dá)式,這是一個(gè)正向預(yù)查,來匹配 Windows 95、Windows 98 以及 Windows NT:
/Windows(?=95 |98 |NT )/
在 VBScript 要進(jìn)行同樣的匹配可以使用下述表達(dá)式:
"Windows(?=95 |98 |NT )"
找到一個(gè)匹配后,緊接匹配到的文字(而不包括預(yù)查中使用的字符)就開始對下一次匹配的搜索。例如,如果上面所示的表達(dá)式匹配到 'Windows 98',則將從 'Windows' 而不是 '98' 之后繼續(xù)查找。
后向引用
正則表達(dá)式一個(gè)最重要的特性就是將匹配成功的模式的某部分進(jìn)行存儲(chǔ)供以后使用這一能力。請回想一下,對一個(gè)正則表達(dá)式模式或部分模式兩邊添加圓括號將導(dǎo)致這部分表達(dá)式存儲(chǔ)到一個(gè)臨時(shí)緩沖區(qū)中。可以使用非捕獲元字符 '?:', '?=', or '?!' 來忽略對這部分正則表達(dá)式的保存。
所捕獲的每個(gè)子匹配都按照在正則表達(dá)式模式中從左至右所遇到的內(nèi)容存儲(chǔ)。存儲(chǔ)子匹配的緩沖區(qū)編號從 1 開始,連續(xù)編號直至最大 99 個(gè)子表達(dá)式。每個(gè)緩沖區(qū)都可以使用 '\n' 訪問,其中 n 為一個(gè)標(biāo)識特定緩沖區(qū)的一位或兩位十進(jìn)制數(shù)。
后向引用一個(gè)最簡單,最有用的應(yīng)用是提供了確定文字中連續(xù)出現(xiàn)兩個(gè)相同單詞的位置的能力。請看下面的句子:
Is is the cost of of gasoline going up up?
根據(jù)所寫內(nèi)容,上面的句子明顯存在單詞多次重復(fù)的問題。如果能有一種方法無需查找每個(gè)單詞的重復(fù)現(xiàn)象就能修改該句子就好了。下面的 JScript 正則表達(dá)式使用一個(gè)子表達(dá)式就可以實(shí)現(xiàn)這一功能。
/\b([a-z]+) \1\b/gi
等價(jià)的 VBScript 表達(dá)式為:
"\b([a-z]+) \1\b"
在這個(gè)示例中,子表達(dá)式就是圓括號之間的每一項(xiàng)。所捕獲的表達(dá)式包括一個(gè)或多個(gè)字母字符,即由'[a-z]+' 所指定的。該正則表達(dá)式的第二部分是對前面所捕獲的子匹配的引用,也就是由附加表達(dá)式所匹配的第二次出現(xiàn)的單詞。'\1'用來指定第一個(gè)子匹配。單詞邊界元字符確保只檢測單獨(dú)的單詞。如果不這樣,則諸如 "is issued" 或 "this is" 這樣的短語都會(huì)被該表達(dá)式不正確地識別。
在 JScript 表達(dá)式中,正則表達(dá)式后面的全局標(biāo)志 ('g') 表示該表達(dá)式將用來在輸入字符串中查找盡可能多的匹配。大小寫敏感性由表達(dá)式結(jié)束處的大小寫敏感性標(biāo)記 ('i') 指定。多行標(biāo)記指定可能出現(xiàn)在換行符的兩端的潛在匹配。對 VBScript 而言,在表達(dá)式中不能設(shè)置各種標(biāo)記,但必須使用 RegExp 對象的屬性來顯式設(shè)置。
使用上面所示的正則表達(dá)式,下面的 JScript 代碼可以使用子匹配信息,在一個(gè)文字字符串中將連續(xù)出現(xiàn)兩次的相同單詞替換為一個(gè)相同的單詞:
var ss = "Is is the cost of of gasoline going up up?.\n"; var re = /\b([a-z]+) \1\b/gim; //
創(chuàng)建正則表達(dá)式樣式. var rv = ss.replace(re,"$1"); //
用一個(gè)單詞替代兩個(gè)單詞.
最接近的等價(jià)? VBScript 代碼如下:
Dim ss, re, rv ss = "Is is the cost of of gasoline going up up?." & vbNewLine Set re = New RegExp re.Pattern = "\b([a-z]+) \1\b" re.Global = True re.IgnoreCase = True re.MultiLine = True rv = re.Replace(ss,"$1")
請注意在 VBScript 代碼中,全局、大小寫敏感性以及多行標(biāo)記都是使用 RegExp 對象的適當(dāng)屬性來設(shè)置的。
在replace 方法中使用 $1 來引用所保存的第一個(gè)子匹配。如果有多個(gè)子匹配,則可以用 $2, $3 等繼續(xù)引用。
后向引用的另一個(gè)用途是將一個(gè)通用資源指示符 (URI) 分解為組件部分。假定希望將下述的URI 分解為協(xié)議 (ftp, http, etc),域名地址以及頁面/路徑:
http://msdn.microsoft.com:80/scripting/default.htm
下面的正則表達(dá)式可以提供這個(gè)功能。對 JScript,為:
/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/
對 VBScript 為:
"(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)"
第一個(gè)附加子表達(dá)式是用來捕獲該 web 地址的協(xié)議部分。該子表達(dá)式匹配位于一個(gè)冒號和兩個(gè)正斜杠之前的任何單詞。第二個(gè)附加子表達(dá)式捕獲該地址的域名地址。該子表達(dá)式匹配不包括 '^'、 '/' 或 ':' 字符的任何字符序列。第三個(gè)附加子表達(dá)式捕獲網(wǎng)站端口號碼,如果指定了該端口號。該子表達(dá)式匹配后跟一個(gè)冒號的零或多個(gè)數(shù)字。最后,第四個(gè)附加子表達(dá)式捕獲由該 web 地址指定的路徑以及\或者頁面信息。該子表達(dá)式匹配一個(gè)和多個(gè)除'#' 或空格之外的字符。
將該正則表達(dá)式應(yīng)用于上面所示的 URI 后,子匹配包含下述內(nèi)容:
RegExp.$1 包含 "http"
RegExp.$2 包含 "msdn.microsoft.com"
RegExp.$3 包含 ":80"
RegExp.$4 包含 "/scripting/default.htm"