小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

正則表達(dá)式

 JT_man 2014-09-04

1、何謂正則表達(dá)式

正則表達(dá)式(regular expression)就是用一個“字符串”來描述一個特征,然后去驗(yàn)證另一個“字符串”是否符合這個特征,即一段字符串的模式。比如,表達(dá)式“ab+” 描述的特征是“一個 'a' 和 任意個 'b' ”,那么 'ab', 'abb', 'abbbbbbbbbb' 都符合這個特征。

      正則表達(dá)式的功能非常強(qiáng)大,打個比方,比如在關(guān)系數(shù)據(jù)庫中,SQL語言的地位是顯赫的,功能是強(qiáng)大的,那么在字符串處理這個領(lǐng)域,正則表達(dá)式可以和SQL語言在關(guān)系數(shù)據(jù)庫中扮演的角色相媲美。從我本人從事多年的軟件開發(fā)實(shí)踐中可以感覺到,正則表達(dá)式是值得每一個從事計(jì)算機(jī)相關(guān)工作的人去學(xué)習(xí)、掌握,我們可以從中受惠。

      正則表達(dá)式可以用來:(1)驗(yàn)證字符串是否符合指定特征,比如驗(yàn)證是否是合法的郵件地址。(2)用來查找字符串,從一個長的文本中查找符合指定特征的字符串,比查找固定字符串更加靈活方便。(3)用來替換,比普通的替換更強(qiáng)大。(4)split字符串。

      正則表達(dá)式學(xué)習(xí)起來其實(shí)是很簡單的,不多的幾個較為抽象的概念也很容易理解。之所以很多人感覺正則表達(dá)式比較復(fù)雜,一方面是因?yàn)榇蠖鄶?shù)的文檔沒有做到由淺入深地講解,概念上沒有注意先后順序,給讀者的理解帶來困難;另一方面,各種引擎自帶的文檔一般都要介紹它特有的功能,然而這部分特有的功能并不是我們首先要理解的。

      平時的開發(fā),常常在Java和.NET平臺下進(jìn)行,這兩個平臺都提供了相應(yīng)的正則表達(dá)式引擎,用起來感覺也很好。在VBA的開發(fā)中,盡管Excel中提供了很多的內(nèi)置函數(shù),幫助我們解決一些字符串處理相關(guān)的問題,但對有些情況,用內(nèi)置的函數(shù)或者編程來解決,常常會感覺到力不從心或者非常繁瑣,而用正則表達(dá)式來解決,卻變得非常簡單。所有我們在這里主要學(xué)習(xí)如何在VBA中使用正則表達(dá)式。

 

2、相關(guān)工具

      工欲善其事,必先利其器,這是我們在使用正則表達(dá)式需要注意的地方,我們做任何事情,若善于利用一些有用的輔助工具,常常可以達(dá)到事半功倍的效果。那么在使用正則表達(dá)式的時候也是一樣,我們需要一些輔助工具來幫助我們更快、更好地完整任務(wù)。我在使用了多種正則表達(dá)式輔助開發(fā)工具后,經(jīng)過綜合地比較、評價(jià),最終鎖定的一款利器,堪比關(guān)羽手中的“青龍偃月刀”,所向披靡。

這款利器就是  RegexBuddy,相關(guān)介紹參見 www.,這是一款共享工具,功能上沒有限制,但只能用7天,要money的,需要注冊,不過大家在網(wǎng)上找regexbuddy   2.3.2   full   version。

下面我們粗略地看一下RegexBuddy有哪些主要功能:
        1、可以很容易地創(chuàng)建你想要的正則表達(dá)式。
        2、可以幫助我們清楚地理解別人寫的正則表達(dá)式的含義。
        3、可以用測試字符串或者測試文件驗(yàn)證正則表達(dá)式是否合乎要求。
        4、自動生成對應(yīng)各種編程語言的程序代碼段,我們只需要寫出符號要求的正則表達(dá)式,對應(yīng)編程語言的代碼段立刻可以生成,這一點(diǎn)非常的方便。
        5、有一個可復(fù)用的正則表達(dá)式庫,放置的都是一些經(jīng)典的、常用的正則表達(dá)式供我們所用。

 從上面的主要功能介紹我們可以看出,RegexBuddy絕對是我們處理正則表達(dá)式的不二選擇,是一款出色的正則表達(dá)式伴侶式工具,鄭重推薦大家使用。

 

3、正則表達(dá)式規(guī)則------字符

     3.1 普通字符
        字母、數(shù)字、漢字、下劃線、以及后邊章節(jié)中沒有特殊定義的標(biāo)點(diǎn)符號,都是"普通字符"。表達(dá)式中的普通字符,在匹配一個字符串的時候,匹配與之相同的一個字符。
        舉例1:表達(dá)式 "c",在匹配字符串 "abcde" 時,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"c";匹配到的位置是:開始于2,結(jié)束于3。(注:下標(biāo)從0開始還是從1開始,因當(dāng)前編程語言的不同而可能不同)
        舉例2:表達(dá)式 "bcd",在匹配字符串 "abcde" 時,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"bcd";匹配到的位置是:開始于1,結(jié)束于4。

3.2 特殊字符
        因?yàn)槲覀儾豢偸亲龊唵嗡阉鳎孕枰A粢恍┳址米魈厥庥猛?。?1個字符有特殊的含義,分別是:"[", "\","^","$",".","|","?","*","+","(" 和")"。 這些特殊字符我們通常稱作元字符。
        如果你想把它們作為一個普通字符來使用,需要在這些符號之前加一個轉(zhuǎn)義字符"\",比如,如果你想匹配"1+1=2",正確的模式應(yīng)該是"1\+1=2",不然,加號"+"就有了特殊的含義。注意,"1+1=2",其中的加號不帶轉(zhuǎn)義字符,但這也是一個有效的正則表達(dá)式,所以在程序執(zhí)行的時候不會有錯誤,但它不會匹配"1+1=2",因?yàn)樘厥庾址?+",它將會在"123+111=234"字符串中,匹配"111=2"。

3.3 不能顯示的字符
模式串中可以使用一些不不能顯示的字符,如:用 \t 來匹配一個 tab 字符 (ASCII 0x09), \r 代表回車 (0x0D) , \n 代表換行 (0x0A). 注意windows操作系統(tǒng)的文本文件中,用 \r\n 分割行,而UNIX 操作系統(tǒng)中使用\n.

 

4、正則表達(dá)式引擎的內(nèi)部工作機(jī)制

了解正則表達(dá)式引擎的內(nèi)部工作機(jī)制將有助于你更好地駕馭正則表達(dá)式,高效、簡潔書寫合理的正則表達(dá)式來完成任務(wù)。

有兩種類型的引擎:文本導(dǎo)向(text-directed)引擎和正則導(dǎo)向(regex-directed)引擎。Jeffrey Friedl把它們稱作DFA和NFA引擎。本文談到的是正則導(dǎo)向引擎。因?yàn)橐恍┓浅S杏玫奶匦裕纭岸栊浴绷吭~(lazy quantifiers)和反向引用(backreferences),只能在正則導(dǎo)向的引擎中實(shí)現(xiàn), 毫不意外這種引擎是目前最流行的引擎。

你可以輕易分辨所使用的引擎是哪一種,若反向引用或“惰性”量詞被實(shí)現(xiàn),則可以肯定你使用的是正則導(dǎo)向引擎。你可以作如下測試:將正則表達(dá)式<<regex|regex not>>應(yīng)用到字符串“regex not”。如果匹配的結(jié)果是regex,則是正則導(dǎo)向引擎。如果結(jié)果是regex not,則是文本導(dǎo)向引擎。

正則導(dǎo)向的引擎總是返回最左邊的匹配

這是非中重要的一點(diǎn):即使在后邊可能發(fā)現(xiàn)一個“更好”的匹配,正則導(dǎo)向的引擎也總是返回最左邊的匹配。

當(dāng)把<<cat>>應(yīng)用到“He captured a catfish for his cat”,引擎先比較<<c>>和“H”,結(jié)果失敗了。于是引擎再比較<<c>>和“e”,也失敗了。直到第四個字符,<<c>>匹配了“c”。<<a>>匹配了第五個字符。到第六個字符<<t>>沒能匹配“p”,也失敗了。引擎再繼續(xù)從第五個字符重新檢查匹配性。直到第十五個字符開始,<<cat>>匹配上了“catfish”中的“cat”,正則表達(dá)式引擎急切的返回第一個匹配的結(jié)果,而不會再繼續(xù)查找是否有其他更好的匹配。

 

5、字符類或者字符集

       字符類(也稱為字符集)是由一對方括號“[]”括起來的字符集合。使用字符集,正則引擎可以只匹配多個字符中的一個。如果你想匹配一個“a”或一個“e”,使用<<[ae]>>。使用<<gr[ae]y>>匹配gray或grey。在你不確定你要搜索的字符是采用美國英語還是英國英語時特別有用。一個字符集只匹配一個字符,<<gr[ae]y>>將不會匹配graay或graey。字符集中的字符順序無關(guān)。

可以使用連字符“-”定義一個字符范圍作為字符集。<<[0-9]>>匹配0到9之間的單個數(shù)字??梢允褂貌恢挂粋€范圍。<<[0-9a-fA-F] >>匹配單個的十六進(jìn)制數(shù)字,并且大小寫不敏感。也可以結(jié)合范圍定義與單個字符定義。<<[0-9a-fxA-FX]>>匹配一個十六進(jìn)制數(shù)字或字母X。再次強(qiáng)調(diào)一下,字符和范圍定義的先后順序?qū)Y(jié)果沒有影響。

字符集的一些應(yīng)用

查找一個可能寫錯的單詞,比如<<sep[ae]r[ae]te>> 或 <<li[cs]en[cs]e>>。
查找程序語言的標(biāo)識符,<<A-Za-z_][A-Za-z_0-9]*>>。(*表示重復(fù)0或多次)
查找C風(fēng)格的十六進(jìn)制數(shù)<<0[xX][A-Fa-f0-9]+>>。(+表示重復(fù)一次或多次)

反字符集

在左方括號“[”后面緊跟一個尖括號“^”,將會對字符集取反。結(jié)果是字符集將匹配任何不在方括號中的字符。不像“.”,取反字符集是可以匹配回車換行符的。

需要記住的很重要的一點(diǎn)是,取反字符集必須要匹配一個字符。<<q[^u]>>并不意味著:匹配一個q,后面沒有u跟著。它意味著:匹配一個q,后面跟著一個不是u的字符。所以它不會匹配“Iraq”中的q,而會匹配“Iraq is a country”中的q和一個空格符。事實(shí)上,空格符是匹配中的一部分,因?yàn)樗且粋€“不是u的字符”。

如果只想匹配一個q,條件是q后面有一個不是u的字符,可以用后面將講到的向前查看來解決。

字符集中的元字符
需要注意的是,在字符集中只有4個 字符具有特殊含義。它們是:“] \ ^ -”?!癩”代表字符集定義的結(jié)束;“\”代表轉(zhuǎn)義;“^”代表取反;“-”代表范圍定義。其他常見的元字符在字符集定義內(nèi)部都是正常字符,不需要轉(zhuǎn)義。例如,要搜索星號*或加號+,你可以用<<[+*]>>。當(dāng)然,如果你對那些通常的元字符進(jìn)行轉(zhuǎn)義,你的正則表達(dá)式一樣會工作得很好,但是這會降低可讀性。

在字符集定義中為了將反斜杠“\”作為一個文字字符而非特殊含義的字符,你需要用另一個反斜杠對它進(jìn)行轉(zhuǎn)義。<<[\\x]>>將會匹配一個反斜杠和一個X?!癩^-”都可以用反斜杠進(jìn)行轉(zhuǎn)義,或者將他們放在一個不可能使用到他們特殊含義的位置。我們推薦后者,因?yàn)檫@樣可以增加可讀性。比如對于字符“^”,將它放在除了左括號“[”后面的位置,使用的都是文字字符含義而非取反含義。如<<[x^]>>會匹配一個x或^。<<[]x]>>會匹配一個“]”或“x”。<<[-x]>>或<<[x-]>>都會匹配一個“-”或“x”。

字符集的簡寫
因?yàn)橐恍┳址浅3S?,所以有一些簡寫方式?BR><<\d>>代表<<[0-9]>>;
<<\w>>代表單詞字符。這個是隨正則表達(dá)式實(shí)現(xiàn)的不同而有些差異。絕大多數(shù)的正則表達(dá)式實(shí)現(xiàn)的單詞字符集都包含了<<A-Za-z0-9_]>>。
<<\s>>代表“白字符”。這個也是和不同的實(shí)現(xiàn)有關(guān)的。在絕大多數(shù)的實(shí)現(xiàn)中,都包含了空格符和Tab符,以及回車換行符<<\r\n>>。
字符集的縮寫形式可以用在方括號之內(nèi)或之外。<<\s\d>>匹配一個白字符后面緊跟一個數(shù)字。<<[\s\d]>>匹配單個白字符或數(shù)字。<<[\da-fA-F]>>將匹配一個十六進(jìn)制數(shù)字。
取反字符集的簡寫
<<[\S]>> = <<[^\s]>>
<<[\W]>> = <<[^\w]>>
<<[\D]>> = <<[^\d]>>

字符集的重復(fù)

如果用“?*+”操作符來重復(fù)一個字符集,將會重復(fù)整個字符集。而不僅是它匹配的那個字符。正則表達(dá)式<<[0-9]+>>會匹配837以及222。
如果僅僅想重復(fù)被匹配的那個字符,可以用向后引用達(dá)到目的。以后將講到向后引用。

 

6、點(diǎn)號"."

      使用“.”匹配幾乎任意字符
      在正則表達(dá)式中,“.”是最常用的元字符之一。不幸的是,它也容易被誤用。

“.”匹配一個單個的字符而不管該字符是什么。唯一的例外是換行符。在本教程中談到的引擎,缺省情況下都是不匹配換行符的。因此在缺省情況下,“.”等價(jià)于字符集[^\n\r](Window)或[^\n]( Unix)。

這個例外是有歷史原因的,因?yàn)樵缙谑褂谜齽t表達(dá)式的工具是基于行的,它們都是一行一行的讀入一個文件,將正則表達(dá)式分別應(yīng)用到每一行上去,在這些工具中,字符串是不包含換行符的。因此“.”也就從不匹配換行符。

現(xiàn)代的工具和語言能夠?qū)⒄齽t表達(dá)式應(yīng)用到很大的字符串甚至整個文件上去。在Perl中,“.”可以匹配換行符的模式被稱作“單行模式”。很不幸,這是一個很容易混淆的名詞。因?yàn)檫€有所謂“多行模式”。多行模式只影響行首行尾的錨定(anchor),而單行模式只影響“.”。

其他語言和正則表達(dá)式庫也采用了Perl的術(shù)語定義。當(dāng)在.NET Framework中使用正則表達(dá)式類時,你可以用類似下面的語句來激活單行模式:Regex.Match(“string”,”regex”,RegexOptions.SingleLine)

保守地使用點(diǎn)號“.”

點(diǎn)號可以說是最強(qiáng)大的元字符。它允許你偷懶:用一個點(diǎn)號,就能匹配幾乎所有的字符。但是問題在于,它也常常會匹配不該匹配的字符。

以一個簡單的例子來說明。如何匹配一個具有“mm/dd/yy”格式的日期,想允許用戶來選擇分隔符。很快能想到的一個方案是<<\d\d.\d\d.\d\d>>??瓷先ニ芷ヅ淙掌凇?2/12/03”。問題在于02512703也會被認(rèn)為是一個有效的日期。
<<\d\d[-/.]\d\d[-/.]\d\d>>看上去是一個好一點(diǎn)的解決方案。記住點(diǎn)號在一個字符集里不是元字符。這個方案遠(yuǎn)不夠完善,它會匹配“99/99/99”。而<<[0-1]\d[-/.][0-3]\d[-/.]\d\d>>又更進(jìn)一步。盡管他也會匹配“19/39/99”。你想要你的正則表達(dá)式達(dá)到如何完美的程度取決于你想達(dá)到什么樣的目的。如果你想校驗(yàn)用戶輸入,則需要盡可能的完美。如果你只是想分析一個已知的源,并且我們知道沒有錯誤的數(shù)據(jù),用一個比較好的正則表達(dá)式來匹配你想要搜尋的字符就已經(jīng)足夠。

 

7、錨(anchor)

      字符串開始和結(jié)束錨(anchor)

錨是一個特別的符號,它不匹配任何字符。相反,他們匹配的是字符之前或之后的位置?!癪”匹配一行字符串第一個字符前的位置。<<^a>>將會匹配字符串“abc”中的a。<<^b>>將不會匹配“abc”中的任何字符。

類似的,$匹配字符串中最后一個字符后面的位置。所以<<c$>>匹配“abc”中的c。

應(yīng)用場合
在編程語言中校驗(yàn)用戶輸入時,使用錨是非常重要的。如果你想校驗(yàn)用戶的輸入為整數(shù),用<<^\d+$>>。
用戶輸入中,常常會有多余的前導(dǎo)空格或結(jié)束空格。你可以用<<^\s*>>和<<\s*$>>來匹配前導(dǎo)空格或結(jié)束空格。

使用“^”和“$”作為行的開始和結(jié)束錨

如果有一個多行的字符串。例如:“first line\r\nsecond line”(其中\(zhòng)r\n表示一個換行符)。常常需要逐行處理而不是處理整個字符串。因此,幾乎所有的正則表達(dá)式引擎都提供一個選項(xiàng),可以擴(kuò)展這兩種錨的含義。“^”可以匹配字串的開始位置(在f之前),以及每一個換行符的后面位置(在\r\n和s之間)。類似的,$會匹配字串的結(jié)束位置(最后一個e之后),以及每個新行符的前面(在e與\r\n之間)。

在.NET中,當(dāng)你使用如下代碼時,將會定義錨匹配每一個換行符的前面和后面位置:Regex.Match("string", "regex", RegexOptions.Multiline) ;string str = Regex.Replace(Original,  "^", "> ", RegexOptions.Multiline)--將會在每行的行首插入“> ”。

字符串絕對開始和結(jié)束錨(anchor)
<<\A>>只匹配整個字符串的開始位置,<<\Z>>只匹配整個字符串的結(jié)束位置。即使你使用了“多行模式”,<<\A>>和<<\Z>>也從不匹配新行符。

 

8、 詞界

       元字符<<\b>>也是一種類似于<<^>>和<<$>>的“錨”,用來匹配單詞邊界位置,匹配長度0。

有4種不同位置常被鑒定為“單詞邊界”:

  • 在字符串的第一個字符前的位置(如果字符串的第一個字符是一個“單詞字符”)
  • 在字符串的最后一個字符后的位置(如果字符串的最后一個字符是一個“單詞字符”)
  • 在一個“單詞字符”和“非單詞字符”之間,其中“非單詞字符”緊跟在“單詞字符”之后
  • 在一個“非單詞字符”和“單詞字符”之間,其中“單詞字符”緊跟在“非單詞字符”后面

“單詞字符”是可以用<<\w>>匹配的字符,“非單詞字符”是可以用<<\W>>匹配的字符。在大多數(shù)的正則表達(dá)式實(shí)現(xiàn)中,“單詞字符”通常包括<<[a-zA-Z0-9_]>>。

例如:<<\b4\b>>能夠匹配單個的4而不會匹配“44”中的4。可以說<<\b>>匹配一個“字母數(shù)字序列”的開始和結(jié)束的位置。

“單詞邊界”的反集為<<\B>>,他要匹配的位置是兩個“單詞字符”之間或者兩個“非單詞字符”之間的位置。

 

9、選擇符

       可以使用選擇符<<|>>來匹配幾個正則表達(dá)式中的一個。

如果要查找文本串“cat”或者“dog”,可以用<<cat|dog>>。如果想有更多的選擇,只要擴(kuò)展列表<<cat|dog|mouse|fish>>。

選擇符在正則表達(dá)式中具有最低的優(yōu)先級,也就是說,它告訴引擎要么匹配選擇符左邊的所有表達(dá)式,要么匹配右邊的所有表達(dá)式??梢杂脠A括號來限制選擇符的作用范圍。如<<\b(cat|dog)\b>>,這樣告訴正則引擎把(cat|dog)當(dāng)成一個正則表達(dá)式單位來處理。

注意正則引擎的“Eager”性

正則引擎找到一個有效的匹配時,會停止搜索。因此在一定條件下,選擇符兩邊的表達(dá)式的順序?qū)Y(jié)果會有影響。假設(shè)用正則表達(dá)式搜索一個編程語言的函數(shù)列表:Get,GetValue,Set或SetValue。一個明顯的解決方案是<<Get|GetValue|Set|SetValue>>。

當(dāng)搜索SetValue時的結(jié)果,因?yàn)?lt;<Get>>和<<GetValue>>都失敗了,而<<Set>>匹配成功。因?yàn)檎齽t導(dǎo)向的引擎的"Eager"性,所以它會返回第一個成功的匹配,就是“Set”,而不去繼續(xù)搜索是否有其他更好的匹配。和期望的相反,正則表達(dá)式并沒有匹配整個字符串。有幾種可能的解決辦法。一是考慮到正則引擎的“Eager”性,改變選項(xiàng)的順序,例如使用<<GetValue|Get|SetValue|Set>>,這樣可以優(yōu)先搜索最長的匹配。也可以把四個選項(xiàng)結(jié)合起來成兩個選項(xiàng):<<Get(Value)?|Set(Value)?>>。因?yàn)閱柼栔貜?fù)符是貪婪的,所以SetValue總會在Set之前被匹配。
一個更好的方案是使用單詞邊界:<<\b(Get|GetValue|Set|SetValue)\b>>或<<\b(Get(Value)?|Set(Value)?\b>>。更進(jìn)一步,既然所有的選擇都有相同的結(jié)尾,可以把正則表達(dá)式優(yōu)化為<<\b(Get|Set)(Value)?\b>>。

 

10、正則表達(dá)式實(shí)戰(zhàn)訓(xùn)練1

光說不練也不行,在講理論的時候得配合實(shí)戰(zhàn)訓(xùn)練,效果才會更好。下面給一個例子,用來學(xué)習(xí)基本的正則編程。

題目:要求取得字符串中的所有數(shù)字。

題目本身比較簡單,我們可以采用常規(guī)的做法,從字符串的第一個字符開始,循環(huán)逐個判斷每一個字符是數(shù)字還是非數(shù)字......

但常規(guī)做法顯得比較麻煩,我們來看如何使用正則表達(dá)式來解決這個問題。

思路有二:
1. 采用查找的方式,匹配到數(shù)字就取出來,最后連成一個結(jié)果。  所用的正則表達(dá)式為  \d+,可以匹配一個或多個連續(xù)的數(shù)字。
2. 采用替換的方式,匹配到非數(shù)字就把它替換成空字符串。所用的正則表達(dá)式為  [^\d]+,可以匹配一個或多個連續(xù)的非數(shù)字。

究竟那種方式更好,大家自己判斷一下就可以了,比較容易的。

 

11、問號的使用

在正則表達(dá)式中,問號使它前面的符號變成可選項(xiàng)。如:<<colou?r>>可以匹配"colour"和"color"。

使用圓括號使一組字符成為可選項(xiàng),問號在圓括號后。如:<<Nov(ember)?>>會匹配"Nov"和"November"。 

在一個正則表達(dá)式中,可以使用多個問號,使多個符號成為可選項(xiàng)。如:<<Feb(ruary)? 23(rd)?>>匹配"February 23rd","February 23","Feb 23rd"和"Feb 23"。 

在這里,要介紹正則表達(dá)式中的一個非常重要的概念------貪婪性。我們這里介紹的第一個貪婪的元字符就是問號。問號讓正則引擎有兩種選擇:盡力去匹配問號前的部分;不匹配問號錢的部分。而正則引擎總是試圖去匹配問號前的部分,只有當(dāng)這樣的嘗試失敗后,才會忽略掉問號前的部分,所以說問號是一個貪婪的元字符。

若用正則式<<Feb 23(rd)?>>去匹配字符串"Today is Feb 23rd, 2003",得到的結(jié)果總是"Feb 23rd"而不是"Feb 23",就是因?yàn)閱柼柕呢澙沸浴?/P>

 

 12、重復(fù)量詞

       使用*和+進(jìn)行重復(fù)

11、中已經(jīng)介紹過一種重復(fù)操作符或者說量詞:問號?。它告訴正則引擎匹配前導(dǎo)字符0次或者1次,效果就是使它成為可選的。
        +告訴引擎匹配前導(dǎo)字符1次或多次;*告訴引擎匹配前導(dǎo)字符0次或多次。

正則式 <[A-Za-z][A-Za-z0-9]*> 匹配沒有屬性的HTML標(biāo)簽,,“<”以及“>”是文字符號。第一個字符集[A-Za-z]匹配一個字母,第二個字符集[A-Za-z0-9]匹配一個字母或數(shù)字,星號重復(fù)第二個字符集一次或多次。因?yàn)槲覀兪褂眯翘?,第二個字符集有0個匹配也是正確的,所以這個正則式可以匹配一個標(biāo)簽"<B>"。匹配字符串"<HTML>"時,第一個字符集匹配"H",星號將會導(dǎo)致第二個字符集重復(fù)匹配三次,匹配到"TML"。

有限重復(fù)

許多現(xiàn)在的正則引擎實(shí)現(xiàn),允許定義對一個字符重復(fù)多少次。語法是:{min,max}。min和max都是非負(fù)整數(shù)。如果逗號有而max被忽略了,則max沒有限制。如果逗號和max都被忽略了,則重復(fù)min次。

因此{0,}*一樣,{1,}+ 的作用一樣。

你可以用\b[1-9][0-9]{3}\b匹配1000~9999之間的數(shù)字(“\b”表示單詞邊界)。\b[1-9][0-9]{2,4}\b匹配一個在100~99999之間的數(shù)字。

注意貪婪性

假設(shè)用一個正則式去匹配一個HTML標(biāo)簽。知道輸入將會是一個有效的HTML文件,因此正則式不需要排除那些無效的標(biāo)簽。所以如果是兩個尖括號之間的內(nèi)容,就應(yīng)該是一個HTML標(biāo)簽。

很多正則式的新手首先會想到用正則式 <.+> ,他們會很驚訝的發(fā)現(xiàn),對于測試字符串,“This is a <EM>first</EM> test”,你可能期望會返回<EM>,然后繼續(xù)進(jìn)行匹配的時候,返回</EM>。但事實(shí)不會。正則式將會匹配“<EM>first</EM>”。顯然這不是我們想要的結(jié)果。

原因在于“+”是貪婪的。也就是說,“+”會導(dǎo)致正則引擎試圖盡可能地重復(fù)前導(dǎo)字符。只有當(dāng)這種重復(fù)會引起整個正則表達(dá)式匹配失敗的情況下,引擎會進(jìn)行回溯。也就是說,它會放棄最后一次的“重復(fù)”,然后處理正則表達(dá)式余下的部分。

和“+”類似,“?”“*”的重復(fù)也是貪婪的。

 

14、分組與后向引用(Grouping and Backreference)

使用圓括號進(jìn)行分組 
   把
部分正則表達(dá)式置于一對圓括號內(nèi)"()",可以將它們形成組。這樣可以對整個組使用一些正則操作,例如重復(fù)操作。這里需要注意的是,只有圓括號才可以用于分組, 方括號和大括號都有不同的用途,不要混淆。

   

使用圓括號創(chuàng)建后向引用

()”在分組的同時,也會創(chuàng)建一個“后向引用”,一個“后向引用”保存的是一個分組內(nèi)正則表達(dá)式匹配的字符串

在定義了一個正則表達(dá)式組的同時,“()”也創(chuàng)建了一個后向引用。一個后向引用保存了由"()"內(nèi)的正則式匹配到的字符串。

除非你使用“非捕獲”的“()”,否則該“()”會形成一個后向引用。這里記住后向引用會減慢正則引擎的速度,因?yàn)樗枰瓿梢恍╊~外的操作。如果你不使用后向引用,你可以使用非捕獲的“()”來提高正則引擎的工作速度,但這樣做的代價(jià)是會使你的正則式的易讀性降低。

正則式"Set(Value)?"匹配"Set"或"SetValue"。在第一種情況中,第一個后向引用(這里也只有一個后向引用)是空的,因?yàn)樗鼪]有匹配任何字符串。在第二種情況中,第一個后向引用將會保存"Value"。
如果你不使用后向引用,你可以把這個正則式優(yōu)化成"Set(?:Value)?",正則式中"("后的問號和冒號是一種特殊的語法格式,它會告訴正則引擎"()"不創(chuàng)建后向引用。注意這里的"("后的"?"和我們在11、中講到的正則表達(dá)式中的問號是不一樣的,因?yàn)?("本身是一個特殊符號,不是一個有效的正則符號。

如何使用后向引用

后向引用允許你重復(fù)使用后向引用中保存的內(nèi)容??梢杂谩癨數(shù)字”的方式進(jìn)行引用。"\1"引用第一個匹配的后向引用組,"\2"引用第二個組,以此類推,"\n"引用第n個組。而"\0"則引用整個被匹配的正則表達(dá)式本身。

在正則式中使用后向引用

假設(shè)你想匹配一個HTML標(biāo)簽的開始標(biāo)簽和結(jié)束標(biāo)簽,以及標(biāo)簽中間的文本。比如<B>This is a test</B>,我們要匹配<B>和</B>以及中間的文字。我們可以用如下正則表達(dá)式:“<([A-Z][A-Z0-9]*)[^>]*>.*?</\1>”
首先,“<”將會匹配“<B>”的第一個字符“<”。然后[A-Z]匹配B,[A-Z0-9]*將會匹配0到多次字母數(shù)字,后面緊接著0到多個非“>”的字符。最后正則表達(dá)式的“>”將會匹配“<B>”的“>”。接下來正則引擎將對結(jié)束標(biāo)簽之前的字符進(jìn)行惰性匹配,直到遇到一個“</”符號。然后正則表達(dá)式中的“\1”表示對前面匹配的組“([A-Z][A-Z0-9]*)”進(jìn)行引用,在本例中,被引用的是標(biāo)簽名“B”。所以需要被匹配的結(jié)尾標(biāo)簽為“</B>”

你可以對相同的后向引用組進(jìn)行多次引用,"([a-c])x\1x\1"將匹配“axaxa”、“bxbxb”以及“cxcxc”。如果用數(shù)字形式引用的組沒有有效的匹配,則引用到的內(nèi)容簡單的為空。

一個后向引用不能用于它自身。"([abc]\1)"是錯誤的。因此你不能將"\0"用于一個正則表達(dá)式匹配本身,它只能用于替換操作中。

后向引用不能用于字符集內(nèi)部。"(a)[\1b]"中的"\1"并不表示后向引用。在字符集內(nèi)部,"\1"可以被解釋為八進(jìn)制形式的轉(zhuǎn)碼。

重復(fù)操作與后向引用

當(dāng)對組使用重復(fù)操作符時,緩存里后向引用內(nèi)容會被不斷刷新,只保留最后匹配的內(nèi)容。如果"()"中的正則式找到一個新的匹配,則后向引用中的內(nèi)容會被覆蓋。"([abc]+) "和"([abc])+"就有著明顯的差別。對于字符串"abc",兩個正則式都會匹配成功。第一個正則式將會把"abc"置于后向引用中,而第二個正則式僅會把"b"置于后向引種中。因?yàn)樵诘诙€正則式中,"+"會導(dǎo)致"()"重復(fù)匹配3次,第一次"a"被保存,第二次"b"被保存,第三次"c"被保存,每一次,前一次的值都被覆蓋。

這也意味著對于字符串"cab=cab", 正則式"([abc]+)=\1"會匹配成功,而"([abc])+=\1"則不能匹配成功。

后向引用使用實(shí)例

當(dāng)編輯文字時,很容易就會輸入重復(fù)單詞,例如"the the"。使用"\b(\w+)\s+\1\b"可以檢測到這些重復(fù)單詞。要刪除第二個單詞,只要簡單的利用替換功能,用"\1"替換就可以了。

 

15、正則表達(dá)式實(shí)戰(zhàn)訓(xùn)練3

問題: 字符串中1個或者連續(xù)多個空格替換成一個Tab鍵
分析: 由于字符串中連續(xù)的空格的個數(shù)不太確定,如果我們逐個字符去分析的話,也是比較麻煩的,但如果用正則去實(shí)現(xiàn),則簡單的多。這也是一個典型的適合用正則解決的問題。解決方式如下:

Sub test()
    Dim RegEx       As Object
 
    Set RegEx = CreateObject("vbscript.regexp")
 
    With RegEx
        .Global = True
        .Pattern = "\s+"
    End With
 
    Range("B1").Value = RegEx.Replace(Range("A1").Text, Chr(9))
    
    Set RegEx = Nothing
End Sub

 

16、正則表達(dá)式實(shí)戰(zhàn)訓(xùn)練4

問題:查找字符串中連續(xù)9個數(shù)字字符并顯示其開始位置。

Sub test()
    Set objRegEx = CreateObject("VBScript.RegExp")
    objRegEx.Global = True
    objRegEx.Pattern = "\d{9}"
    
    strSearchString = "aaaaaaa123456789qqqqqqqqqqqq234567891aaaa12345678"
    
    Set colMatches = objRegEx.Execute(strSearchString)
    
    If colMatches.Count > 0 Then
       strMessage = "The following user accounts were found:" & vbCrLf
       For Each strMatch In colMatches
           strMessage = strMessage & strMatch.Value & " (character position " & _
               strMatch.FirstIndex & ")" & vbCrLf
       Next
    End If
    
    MsgBox strMessage
End Sub

 

17、實(shí)用的正則查找封裝函數(shù)

這是一個封裝的查找函數(shù),使用正則實(shí)現(xiàn)查找功能,函數(shù)實(shí)現(xiàn)如下,使用時,把下面的函數(shù)置于VBA工程的普通模塊中,就可以在代碼中引用或者在單元格中直接使用函數(shù)

Function RegExpFind(LookIn As String, PatternStr As String, Optional Pos, Optional MatchCase As Boolean = True)
    ' This function uses Regular Expressions to parse a string (LookIn), and return matches to a
    ' pattern (PatternStr).  Use Pos to indicate which match you want:
    ' Pos omitted               : function returns a zero-based array of all matches
    ' Pos = 0                   : the last match
    ' Pos = 1                   : the first match
    ' Pos = 2                   : the second match
    ' Pos =   : the Nth match
    ' If Pos is greater than the number of matches, is negative, or is non-numeric, the function
    ' returns an empty string.  If no match is found, the function returns an empty string
    
    ' If MatchCase is omitted or True (default for RegExp) then the Pattern must match case (and
    ' thus you may have to use [a-zA-Z] instead of just [a-z] or [A-Z]).
    
    ' If you use this function in Excel, you can use range references for any of the arguments.
    ' If you use this in Excel and return the full array, make sure to set up the formula as an
    ' array formula.  If you need the array formula to go down a column, use TRANSPOSE()
    
    Dim RegX As Object
    Dim TheMatches As Object
    Dim Answer() As String
    Dim Counter As Long
    
    ' Evaluate Pos.  If it is there, it must be numeric and converted to Long
    If Not IsMissing(Pos) Then
        If Not IsNumeric(Pos) Then
            RegExpFind = ""
            Exit Function
        Else
            Pos = CLng(Pos)
        End If
    End If
    
    ' Create instance of RegExp object
    Set RegX = CreateObject("VBScript.RegExp")
    With RegX
        .Pattern = PatternStr
        .Global = True
        .IgnoreCase = Not MatchCase
    End With
        
    ' Test to see if there are any matches
    If RegX.test(LookIn) Then
        
        ' Run RegExp to get the matches, which are returned as a zero-based collection
        Set TheMatches = RegX.Execute(LookIn)
        
        ' If Pos is missing, user wants array of all matches.  Build it and assign it as the
        ' function's return value
        If IsMissing(Pos) Then
            ReDim Answer(0 To TheMatches.Count - 1) As String
            For Counter = 0 To UBound(Answer)
                Answer(Counter) = TheMatches(Counter)
            Next
            RegExpFind = Answer
        
        ' User wanted the Nth match (or last match, if Pos = 0).  Get the Nth value, if possible
        Else
            Select Case Pos
                Case 0                          ' Last match
                    RegExpFind = TheMatches(TheMatches.Count - 1)
                Case 1 To TheMatches.Count      ' Nth match
                    RegExpFind = TheMatches(Pos - 1)
                Case Else                       ' Invalid item number
                    RegExpFind = ""
            End Select
        End If
    
    ' If there are no matches, return empty string
    Else
        RegExpFind = ""
    End If
    
    ' Release object variables
    Set RegX = Nothing
    Set TheMatches = Nothing
    
End Function

 

18、實(shí)用的正則替換封裝函數(shù)

這是一個封裝的替換函數(shù),使用正則實(shí)現(xiàn)替換功能,函數(shù)實(shí)現(xiàn)如下,使用時,把下面的函數(shù)置于VBA工程的普通模塊中,就可以在代碼中引用或者在單元格中直接使用函數(shù)

Function RegExpReplace(LookIn As String, PatternStr As String, Optional ReplaceWith As String = "", Optional ReplaceAll As Boolean = True, Optional MatchCase As Boolean = True)
        ' This function uses Regular Expressions to parse a string, and replace parts of the string
        ' matching the specified pattern with another string.  The optional argument ReplaceAll controls
        ' whether all instances of the matched string are replaced (True) or just the first instance (False)
    
        ' By default, RegExp is case-sensitive in pattern-matching.  To keep this, omit MatchCase or
        ' set it to True
    
        ' If you use this function from Excel, you may substitute range references for all the arguments

        Dim RegX As Object
    
        Set RegX = CreateObject("VBScript.RegExp")
        With RegX
                .Pattern = PatternStr
                .Global = ReplaceAll
                .IgnoreCase = Not MatchCase
        End With
    
        RegExpReplace = RegX.Replace(LookIn, ReplaceWith)
        Set RegX = Nothing
End Function

 

19、正則表達(dá)式實(shí)戰(zhàn)訓(xùn)練5

問題:判斷一個郵件地址格式是否有效

Private Sub CommandButton1_Click()
    If IsValidEmail(TextBox1) Then 'Email address is stored in TextBox1
        MsgBox "valid email address."
    Else
        MsgBox "Not a valid email address."
    End If
End Sub
 
Private Function IsValidEmail(value As StringAs Boolean
    Dim RE As Object
    Set RE = CreateObject("vbscript.RegExp")
    RE.Pattern = "^[a-zA-Z0-9\._-]+@([a-zA-Z0-9_-]+\.)+([a-zA-Z]{2,3})$"
    IsValidEmail = RE.Test(value)
    Set RE = Nothing
End Function

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多