精通正則表達(dá)式_第1頁
精通正則表達(dá)式_第2頁
精通正則表達(dá)式_第3頁
精通正則表達(dá)式_第4頁
精通正則表達(dá)式_第5頁
已閱讀5頁,還剩397頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

精通正則表達(dá)式第3版目錄\h第1章正則表達(dá)式入門\h解決實(shí)際問題\h作為編程語言的正則表達(dá)式\h以文件名做類比\h以語言做類比\h正則表達(dá)式的思維框架\h對(duì)于有部分經(jīng)驗(yàn)的讀者\(yùn)h檢索文本文件:Egrep\hEgrep元字符\h行的起始和結(jié)束\h字符組\h用點(diǎn)號(hào)匹配任意字符\h多選結(jié)構(gòu)\h忽略大小寫\h單詞分界符\h小結(jié)\h可選項(xiàng)元素\h其他量詞:重復(fù)出現(xiàn)\h括號(hào)及反向引用\h神奇的轉(zhuǎn)義\h基礎(chǔ)知識(shí)拓展\h語言的差異\h正則表達(dá)式的目標(biāo)\h更多的例子\h正則表達(dá)式術(shù)語匯總\h改進(jìn)現(xiàn)狀\h總結(jié)\h一家之言\h第2章入門示例拓展\h關(guān)于這些例子\hPerl簡單入門\h使用正則表達(dá)式匹配文本\h向更實(shí)用的程序前進(jìn)\h成功匹配的副作用\h錯(cuò)綜復(fù)雜的正則表達(dá)式\h暫停片刻\h使用正則表達(dá)式修改文本\h例子:公函生成程序\h舉例:修整股票價(jià)格\h自動(dòng)的編輯操作\h處理郵件的小工具\(yùn)h用環(huán)視功能為數(shù)值添加逗號(hào)\hText-to-HTML轉(zhuǎn)換\h回到單詞重復(fù)問題\h第3章正則表達(dá)式的特性和流派概覽\h在正則的世界中漫步\h正則表達(dá)式的起源\h最初印象\h正則表達(dá)式的注意事項(xiàng)和處理方式\h集成式處理\h程序式處理和面向?qū)ο笫教幚韁h查找和替換\h其他語言中的查找和替換\h注意事項(xiàng)和處理方式:小結(jié)\h字符串,字符編碼和匹配模式\h作為正則表達(dá)式的字符串\h字符編碼\hUnicode\h正則模式和匹配模式\h常用的元字符和特性\h字符表示法\h字符組及相關(guān)結(jié)構(gòu)\h錨點(diǎn)及其他“零長度斷言”\h注釋和模式修飾符\h分組,捕獲,條件判斷和控制\h高級(jí)話題引導(dǎo)\h第4章表達(dá)式的匹配原理\h發(fā)動(dòng)引擎\h兩類引擎\h新的標(biāo)準(zhǔn)\h正則引擎的分類\h幾句題外話\h測試引擎的類型\h匹配的基礎(chǔ)\h關(guān)于范例\h規(guī)則1:優(yōu)先選擇最左端的匹配結(jié)果\h引擎的構(gòu)造\h規(guī)則2:標(biāo)準(zhǔn)量詞是匹配優(yōu)先的\h表達(dá)式主導(dǎo)與文本主導(dǎo)\hNFA引擎:表達(dá)式主導(dǎo)\hDFA引擎:文本主導(dǎo)\h第一想法:比較NFA與DFA\h回溯\h真實(shí)世界中的例子:面包屑\h回溯的兩個(gè)要點(diǎn)\h備用狀態(tài)\h回溯與匹配優(yōu)先\h關(guān)于匹配優(yōu)先和回溯的更多內(nèi)容\h匹配優(yōu)先的問題\h多字符“引文”\h使用忽略優(yōu)先量詞\h匹配優(yōu)先和忽略優(yōu)先都期望獲得匹配\h匹配優(yōu)先、忽略優(yōu)先和回溯的要旨\h占有優(yōu)先量詞和固化分組\h占有優(yōu)先量詞,?+、*+、++和{m,n}+\h環(huán)視中的回溯\h多選結(jié)構(gòu)也是匹配優(yōu)先的嗎\h發(fā)掘有序多選結(jié)構(gòu)的價(jià)值\hNFA、DFA和POSIX\h最左最長規(guī)則\hPOSIX和最左最長規(guī)則\h速度和效率\h小結(jié):NFA與DFA的比較\h總結(jié)\h第5章正則表達(dá)式實(shí)用技巧\h正則表達(dá)式的平衡法則\h若干簡單的例子\h匹配連續(xù)行(續(xù)前)\h匹配IP地址\h處理文件名\h匹配對(duì)稱的括號(hào)\h防備不期望的匹配\h匹配分隔符之內(nèi)的文本\h了解數(shù)據(jù),做出假設(shè)\h去除文本首尾的空白字符\hHTML相關(guān)范例\h匹配HTMLTag\h匹配HTMLLink\h檢查HTTPURL\h驗(yàn)證主機(jī)名\h在真實(shí)世界中提取URL\h擴(kuò)展的例子\h保持?jǐn)?shù)據(jù)的協(xié)調(diào)性\h解析CSV文件\h第6章打造高效正則表達(dá)式\h典型示例\h稍加修改——先邁最好使的腿\h效率vs準(zhǔn)確性\h繼續(xù)前進(jìn)——限制匹配優(yōu)先的作用范圍\h實(shí)測\h全面考察回溯\hPOSIXNFA需要更多處理\h無法匹配時(shí)必須進(jìn)行的工作\h看清楚一點(diǎn)\h多選結(jié)構(gòu)的代價(jià)很高\(yùn)h性能測試\h理解測量對(duì)象\hPHP測試\hJava測試\hVB.NET測試\hRuby測試\hPython測試\hTcl測試\h常見優(yōu)化措施\h有得必有失\h優(yōu)化各有不同\h正則表達(dá)式的應(yīng)用原理\h應(yīng)用之前的優(yōu)化措施\h通過傳動(dòng)裝置進(jìn)行優(yōu)化\h優(yōu)化正則表達(dá)式本身\h提高表達(dá)式速度的訣竅\h常識(shí)性優(yōu)化\h將文字文本獨(dú)立出來\h將錨點(diǎn)獨(dú)立出來\h忽略優(yōu)先還是匹配優(yōu)先?具體情況具體分析\h拆分正則表達(dá)式\h模擬開頭字符識(shí)別\h使用固化分組和占有優(yōu)先量詞\h主導(dǎo)引擎的匹配\h消除循環(huán)\h方法1:依據(jù)經(jīng)驗(yàn)構(gòu)建正則表達(dá)式\h真正的“消除循環(huán)”解法\h方法2:自頂向下的視角\h方法3:匹配主機(jī)名\h觀察\h使用固化分組和占有優(yōu)先量詞\h簡單的消除循環(huán)的例子\h消除C語言注釋匹配的循環(huán)\h流暢運(yùn)轉(zhuǎn)的表達(dá)式\h引導(dǎo)匹配的工具\(yùn)h引導(dǎo)良好的正則表達(dá)式速度很快\h完工\h總結(jié):開動(dòng)你的大腦\h第7章Perl\h作為語言組件的正則表達(dá)式\hPerl的長處\hPerl的短處\hPerl的正則流派\h正則運(yùn)算符和正則文字\h正則文字的解析方式\h正則修飾符\h正則表達(dá)式相關(guān)的Perl教義\h表達(dá)式應(yīng)用場合\h動(dòng)態(tài)作用域及正則匹配效應(yīng)\h匹配修改的特殊變量\hqr/…/運(yùn)算符與regex對(duì)象\h構(gòu)建和使用regex對(duì)象\h探究regex對(duì)象\h用regex對(duì)象提高效率\hMatch運(yùn)算符\hMatch的正則運(yùn)算元\h指定目標(biāo)運(yùn)算元\hMatch運(yùn)算符的不同用途\h迭代匹配:ScalarContext,使用/g\hMatch運(yùn)算符與環(huán)境的關(guān)系\hSubstitution運(yùn)算符\h運(yùn)算元replacement\h/e修飾符\h應(yīng)用場合與返回值\hSplit運(yùn)算符\hSplit基礎(chǔ)知識(shí)\h返回空元素\hSplit中的特殊Regex運(yùn)算元\hSplit中帶捕獲型括號(hào)的match運(yùn)算元\h巧用Perl的專有特性\h用動(dòng)態(tài)正則表達(dá)式結(jié)構(gòu)匹配嵌套結(jié)構(gòu)\h使用內(nèi)嵌代碼結(jié)構(gòu)\h在內(nèi)嵌代碼結(jié)構(gòu)中使用local函數(shù)\h關(guān)于內(nèi)嵌代碼和my變量的忠告\h使用內(nèi)嵌代碼匹配嵌套結(jié)構(gòu)\h正則文字重載\h正則文字重載的問題\h模擬命名捕獲\h效率\h辦法不只一種\h表達(dá)式編譯、/o修飾符、qr/…/和效率\h理解“原文”副本\hStudy函數(shù)\h性能測試\h正則表達(dá)式調(diào)試信息\h結(jié)語\h第8章Java\hJava的正則流派\hJava對(duì)\p{…}和\P{…}的支持\hUnicode行終結(jié)符\h使用java.util.regex\hThePpile()Factory\hPattern的matcher方法\hMatcher對(duì)象\h應(yīng)用正則表達(dá)式\h查詢匹配結(jié)果\h簡單查找-替換\h高級(jí)查找-替換\h原地查找-替換\h方法鏈\h構(gòu)建掃描程序\hMatcher的其他方法\hPattern的其他方法\hPattern的split方法,單個(gè)參數(shù)\hPattern的split方法,兩個(gè)參數(shù)\h拓展示例\h為ImageTag添加寬度和高度屬性\h對(duì)于每個(gè)Matcher,使用多個(gè)Pattern校驗(yàn)HTML\h解析CSV文檔\hJava版本差異\h1.4.2和1.5.0之間的差異\h1.5.0和1.6之間的差異\h第9章.NET\h.NET的正則流派\h對(duì)于流派的補(bǔ)充\h使用.NET正則表達(dá)式\h正則表達(dá)式快速入門\h包概覽\h核心對(duì)象概覽\h核心對(duì)象詳解\h創(chuàng)建Regex對(duì)象\h使用Regex對(duì)象\h使用Match對(duì)象\h使用Group對(duì)象\h靜態(tài)“便捷”函數(shù)\h正則表達(dá)式緩存\h支持函數(shù)\h.NET高級(jí)話題\h正則表達(dá)式裝配件\h匹配嵌套結(jié)構(gòu)\hCapture對(duì)象\h第10章PHP\hPHP的正則流派\hPreg函數(shù)接口\h“pattern”參數(shù)\hPreg函數(shù)羅列\(zhòng)hpreg_match\hpreg_match_all\hpreg_replace\hpreg_replace_callback\hpreg_split\hpreg_grep\hpreg_quote\h“缺失”的preg函數(shù)\hpreg_regex_to_pattern\h對(duì)未知的Pattern參數(shù)進(jìn)行語法檢查\h對(duì)未知正則表達(dá)式進(jìn)行語法檢查\h遞歸的正則表達(dá)式\h匹配嵌套括號(hào)內(nèi)的文本\h不能回溯到遞歸調(diào)用之內(nèi)\h匹配一組嵌套的括號(hào)\h效率\h模式修飾符S:“研究”\h擴(kuò)展示例\h用PHP解析CSV\h檢查taggeddata的嵌套正確性注:原文檔電子版(非掃描),需要的請(qǐng)下載本文檔后留言謝謝。第1章正則表達(dá)式入門IntroductiontoRegularExpressions想象一下這幅圖景:你需要檢索某臺(tái)Web服務(wù)器上的頁面中的重復(fù)單詞(例如“thisthis”),進(jìn)行大規(guī)模文本編輯時(shí),這是一項(xiàng)常見的任務(wù)。程序必須滿足下面的要求:●能檢查多個(gè)文件,挑出包含重復(fù)單詞的行,高亮標(biāo)記每個(gè)重復(fù)單詞(使用標(biāo)準(zhǔn)ANSI的轉(zhuǎn)義字符序列(escapesequence)),同時(shí)必須顯示這行文字來自哪個(gè)文件。●能跨行查找,即使兩個(gè)單詞一個(gè)在某行末尾而另一個(gè)在下一行的開頭,也算重復(fù)單詞?!衲苓M(jìn)行不區(qū)分大小寫的查找,例如‘Thethe…’,重復(fù)單詞之間可以出現(xiàn)任意數(shù)量的空白字符(空格符、制表符、換行符之類)(譯注1)?!衲懿檎矣肏TMLtag分隔的重復(fù)單詞。HTMLtag用于標(biāo)記互聯(lián)網(wǎng)頁上的文本,例如,粗體單詞是這樣表示的:‘…itis<B>very</B>veryimportant…’。這些問題并不容易解決,但又不能不解決。我在寫作本書的手稿時(shí),曾用一個(gè)工具來檢查已經(jīng)寫好的部分,我驚奇地發(fā)現(xiàn),其中竟有那么多的重復(fù)單詞。能夠解決這種問題的編程語言有許多,但是用支持正則表達(dá)式的語言來處理會(huì)相當(dāng)簡單。正則表達(dá)式(RegularExpression)是強(qiáng)大、便捷、高效的文本處理工具。正則表達(dá)式本身,加上如同一門袖珍編程語言的通用模式表示法(generalpatternnotation),賦予使用者描述和分析文本的能力。配合上特定工具提供的額外支持,正則表達(dá)式能夠添加、刪除、分離、疊加、插入和修整各種類型的文本和數(shù)據(jù)。正則表達(dá)式的使用難度只相當(dāng)于文本編輯器的搜索命令,但功能卻與完整的文本處理語言一樣強(qiáng)大。本書將向讀者展示正則表達(dá)式提高生產(chǎn)率的諸多辦法。它會(huì)教導(dǎo)讀者如何學(xué)會(huì)用正則表達(dá)式來思考(thinkregularexpressions),以便于掌握它們,充分利用它們的強(qiáng)大功能。如果使用當(dāng)今流行的程序設(shè)計(jì)語言,解決重復(fù)單詞問題的完整程序可能僅僅只需要幾行代碼。使用一個(gè)正則表達(dá)式的搜索和替換命令,讀者就可以查找文檔中的重復(fù)單詞,并把它們標(biāo)記為高亮。加上另一個(gè),你可以刪除所有不包含重復(fù)單詞的行(只留下需要在結(jié)果中出現(xiàn)的行)。最后,利用第三個(gè)正則表達(dá)式,你可以確保結(jié)果中的所有行都以它所在文件的名字開頭。在下一章里,我們會(huì)看到用Perl和Java編寫的程序。宿主語言(例如Perl、Java以及VB.NET)提供了外圍的處理支持,但是真正的能力來自正則表達(dá)式。為了駕馭這種語言,滿足自己的需求,讀者必須知道如何構(gòu)建正則表達(dá)式,才能識(shí)別符合要求的文本,同時(shí)忽略不需要的文本。然后,就可以把表達(dá)式和語言支持的構(gòu)建方式結(jié)合起來,真正處理這些文本(加入合適的高亮標(biāo)記代碼,刪除文本,修改文本,等等)。解決實(shí)際問題SolvingRealProblems掌握正則表達(dá)式,可能帶來超乎你之前想象的文本處理能力。每一天,我都依靠正則表達(dá)式解決各種大大小小的問題(通常的情況是,問題本身并不復(fù)雜,但沒有正則表達(dá)式就成了大問題)。要說明正則表達(dá)式的價(jià)值,可以舉一個(gè)用正則表達(dá)式解決大而重要的問題的例子,但是它不一定能代表正則表達(dá)式在平時(shí)解決的那些“不值一提”(uninteresting)的問題。這里的“不值一提”是指這類問題并不能成為談資,可是不解決它們,你就沒法繼續(xù)干活。舉個(gè)簡單的例子,我需要檢查許多文件(事實(shí)上,本書的手稿存放在70個(gè)文件中),確保每一行中‘SetSize’出現(xiàn)的次數(shù)與‘ResetSize’的一樣多。為了應(yīng)付復(fù)雜的情況,我還需要考慮大小寫的情況(舉例來說,‘setSIZE’也算做‘SetSize’)。人工檢查32000行文字顯然不現(xiàn)實(shí)。即便使用文本編輯器的“單詞查找”功能,也不夠方便,尤其是對(duì)所有文件進(jìn)行同樣的操作,何況還需要考慮所有可能的大小寫情況。正則表達(dá)式就是解決這個(gè)問題的靈丹妙藥。只需要一個(gè)簡單的命令,我就能夠檢查所有的文件,獲得我需要知道的結(jié)果。時(shí)間是:寫命令大概15秒,檢索所有的數(shù)據(jù)實(shí)際只花了2秒。這真是棒極了(如果您想知道這是怎么做到的,不妨現(xiàn)在就翻到第36頁)!再舉一個(gè)例子,我曾幫助一個(gè)朋友處理遠(yuǎn)端機(jī)器上的某些E-mail,他希望我把他郵箱文件中的消息作為列表發(fā)送給他。我可以把整個(gè)文件導(dǎo)入文本編輯器,手工刪除所有信息,只留下郵件頭中的幾行,作為內(nèi)容的列表。盡管文件不是很大,連接速度也不算慢,這樣的任務(wù)還是很耗費(fèi)時(shí)間而且很乏味。而且,窺見他的郵件正文,也令我尷尬。正則表達(dá)式再一次提供了幫助!我用一個(gè)簡單的命令(使用本章稍后提到的一個(gè)常用工具egrep)顯示每封郵件的From:和Subject:字段。為了告訴egrep我需要提取哪些行,我使用了正則表達(dá)式「^(From|Sbuject):」。朋友得到這個(gè)列表之后,讓我找一封特殊的(5000行?。┼]件。使用文本編輯器或者郵件系統(tǒng)來提取一封郵件無疑非常耗時(shí)。相反,我借助另一個(gè)工具(叫做sed),同樣使用正則表達(dá)式來描述文件中我需要的內(nèi)容。這樣,我能迅速而方便地提取和發(fā)送需要的郵件。使用正則表達(dá)式節(jié)省下來的時(shí)間或許并不能讓人“激動(dòng)”,但總比把時(shí)間消耗在文本編輯器中要好。如果我不知道有正則表達(dá)式這種玩意兒,根本就不會(huì)想到還有別的解決辦法。所以,這個(gè)故事告訴我們,正則表達(dá)式和相關(guān)的工具能夠讓我們以可能未曾想過的方式來解決問題。一旦掌握了正則表達(dá)式,你就會(huì)知道到它簡直是工具中的無價(jià)之寶,你也難以想象之前那些沒有正則表達(dá)式的日子是怎么度過的(注1)。全面掌握正則表達(dá)式是很有用的。本書提供了掌握這種技能所需要的信息,我同時(shí)也希望,這本書也提供了促使你學(xué)習(xí)的動(dòng)機(jī)。作為編程語言的正則表達(dá)式RegularExpressionsasaLanguage如果沒有正則表達(dá)式相關(guān)經(jīng)驗(yàn),讀者可能無法理解上個(gè)例子中正則表達(dá)式「^(From|Subject):」的意義,但是這個(gè)表達(dá)式并沒有什么神奇之處。其實(shí)魔術(shù)本身也不神奇,只是缺乏訓(xùn)練的普通觀眾不明白魔術(shù)師掌握的那些技巧而已。如果你也懂得如何在手中藏一張牌,那么,熟練之后,你也可以“變魔術(shù)”。外語也是這樣——一旦掌握了一門外語,你就不會(huì)覺得它像天書了。以文件名做類比TheFilenameAnalogy選擇這本書的讀者,大概對(duì)“正則表達(dá)式”多少有點(diǎn)認(rèn)識(shí)。即便沒有,也應(yīng)該熟悉其中的基本概念。我們都知道,report.txt是一個(gè)文件名,但是,如果你用過Unix或者DOS/Windows的話,就會(huì)知道“*.txt”能夠用來選擇多個(gè)文件。在此類文件名(稱為“文件群組”fileglobs或者“通配符”wildcards)中,有些字符具有特殊的意義。星號(hào)表示“任意文本”,問號(hào)表示“任意單個(gè)字符”。所以,文件群組“*.txt”以能夠匹配字符的「*」符號(hào)開頭,以普通文字「.txt」結(jié)尾,所以,它的意思是:選擇以任意文本開頭,以.txt結(jié)尾的所有文件。大多數(shù)系統(tǒng)都提供了少量的附加特殊字符(additionalspecialcharacters),但是,總的來說,這些文件名模式(filenamepatterns)的表達(dá)能力還很有限。不過,因?yàn)檫@類問題的領(lǐng)域很狹窄——只涉及文件名,所以這算不上缺陷。不過,處理普通的文本就沒有這么簡單了。散文、詩、程序代碼、報(bào)表、HTML、表格、單詞表……到你想得出的任何文本。如果某種特殊的需求足夠?qū)I(yè),例如“選擇文件”,我們可以發(fā)明一些特殊的辦法和工具來解決問題。不過,近年來,一種“通用的模式語言”(generalizedpatternlanguage)已經(jīng)發(fā)展起來,它功能強(qiáng)大,描述能力也很強(qiáng),可以用來解決各種問題。不同的程序以不同的方式來實(shí)現(xiàn)和使用這種語言,但是綜合來說,這種功能強(qiáng)大的模式語言和模式本身被稱為“正則表達(dá)式”(regularexpression)。以語言做類比TheLanguageAnalogy完整的正則表達(dá)式由兩種字符構(gòu)成。特殊字符(specialcharacters,例如文件名例子中的*)稱為“元字符”(metacharacters),其他為“文字”(literal),或者是普通文本字符(normaltextcharacters)。正則表達(dá)式與文件名模式(filenamepattern)的區(qū)別就在于,正則表達(dá)式的元字符提供了更強(qiáng)大的描述能力。文件名模式只為有限的需求提供了有限的元字符,但是正則表達(dá)式“語言”為高級(jí)應(yīng)用提供了豐富而且描述力極強(qiáng)的元字符。為了便于理解,我們可以把正則表達(dá)式想象為普通的語言,普通字符對(duì)應(yīng)普通語言中的單詞,而元字符對(duì)應(yīng)語法。根據(jù)語言的規(guī)則,按照語法把單詞組合起來,就會(huì)得到能傳達(dá)思想的文本。在E-mail的例子中,我用正則表達(dá)式來尋找以‘From:’或者‘Subject:’開頭的行。下畫線標(biāo)注的就是特殊字符,稍后我們將解釋它們的含義。就像學(xué)習(xí)任何一門外語一樣,第一眼看上去,正則表達(dá)式很不好理解。這也是那些對(duì)它只有粗淺了解或者根本不了解的人覺得正則表達(dá)式很神奇的原因。但是,就像學(xué)日語的人很快就能理解正規(guī)表現(xiàn)は簡簡だよ?。ㄗ?)一樣,讀者很快也能夠徹底明白下面這個(gè)正則表達(dá)式的含義:s!<emphasis>([0-9]+(\.[0-9]+){3})</emphasis>!<inet>$1</inet>!這個(gè)例子取自一個(gè)Perl腳本,我的編輯器用它來修改手稿。手稿的作者錯(cuò)誤地使用了<emphasis>這個(gè)tag來標(biāo)注IP地址(類似2這樣由數(shù)字和點(diǎn)號(hào)構(gòu)成的字符串)。其中的奧妙就在于使用Perl的文本替換命令,使用:「<emphasis>([0-9]+(\.[0-9]+){3})</emphasis>」把IP地址兩端的tag替換為<inet>,而不改動(dòng)其他的<emphasis>標(biāo)簽。在后面的章節(jié)中,讀者會(huì)了解這個(gè)表達(dá)式的構(gòu)造細(xì)節(jié),然后就能按照自己的需求,在自己的應(yīng)用程序或者開發(fā)語言中應(yīng)用這些技巧。本書的目的你或許不需要重復(fù)把<emphasis>替換為<inet>的工作,不過很可能需要解決“把這些文字替換為那些文字”的問題。本書的目的不是提供具體問題的解決辦法,而是教會(huì)讀者利用正則表達(dá)式來思考,解決遇到的各種問題。正則表達(dá)式的思維框架TheRegular-ExpressionFrameofMind我們將會(huì)看到,完整的正則表達(dá)式由小的構(gòu)建模塊單元(buildingblockunit)組成。每個(gè)單獨(dú)的構(gòu)建模塊都很簡單,不過因?yàn)樗鼈兡軌蛞詿o窮多種方式組合,將它們結(jié)合起來實(shí)現(xiàn)特殊目標(biāo)必須依靠經(jīng)驗(yàn)。所以,本章提供了有關(guān)正則表達(dá)式的若干概念的總體描述。這一章并沒有艱深的內(nèi)容,而是為本書其余章節(jié)的知識(shí)打下基礎(chǔ),在深入探索正則表達(dá)式之前,把相關(guān)事宜闡釋清楚。某些例子看起來可能有點(diǎn)無聊(因?yàn)樗鼈兇_實(shí)無聊),但它們代表了一類需要完成的任務(wù),只是讀者目前可能還沒有意識(shí)到。即使覺得每個(gè)例子的意義都不大也不必?fù)?dān)心,慢慢理解其中的道理就好。這就是本章的目的。對(duì)于有部分經(jīng)驗(yàn)的讀者IfYouHaveSomeRegular-ExpressionExperience如果讀者已經(jīng)熟悉正則表達(dá)式,這些綜述便沒有太大價(jià)值,但務(wù)必不要忽略它們。你或許明白某些元字符的基本意義,但某些思維和看待正則表達(dá)式的方式可能是你不了解的。就像真正懂演奏和僅僅會(huì)彈奏之間差別迥異一樣,了解正則表達(dá)式和真正理解正則表達(dá)式并不是一回事。某些內(nèi)容可能會(huì)重復(fù)讀者已經(jīng)了解的知識(shí),但方式可能與之前的不同,而且這些方式正是真正理解正則表達(dá)式的第一步。檢索文本文件:EgrepSearchingTextFiles:Egrep文本檢索是正則表達(dá)式最簡單的應(yīng)用之一——許多文本編輯器和文字處理軟件都提供了正則表達(dá)式檢索的功能。最簡單的就是egrep。在指定了正則表達(dá)式和需要檢索的文件之后,egrep會(huì)嘗試用正則表達(dá)式來匹配每個(gè)文件的每一行,并顯示能夠匹配的行。許多系統(tǒng)——例如DOS、MacOS、Windows、Unix等等——都對(duì)應(yīng)有免費(fèi)提供的egrep。在本書的網(wǎng)頁http://上可以找到獲得對(duì)應(yīng)讀者操作系統(tǒng)的egrep拷貝的鏈接?;氐降?頁的E-mail的例子,真正用來從E-mail文件中提取結(jié)果的命令如圖1-1所示。egrep把第一個(gè)命令行參數(shù)視為一個(gè)正則表達(dá)式,剩下的參數(shù)作為待搜檢索的文件名。注意,圖1-1中的單引號(hào)并不是正則表達(dá)式的一部分,而是根據(jù)commandshell需要添加的(注3)。使用egrep時(shí),我通常用單引號(hào)來包圍正則表達(dá)式。如果要在支持對(duì)正則表達(dá)式提供了完整支持的程序設(shè)計(jì)語言中使用正則表達(dá)式——這是下一章開頭的內(nèi)容,重要的問題是知道特殊字符有哪些,具體文本是什么,針對(duì)什么對(duì)象(什么表達(dá)式,什么工具軟件),以及按何種順序解釋這些字符。圖1-1:通過命令行調(diào)用egrep我們馬上就能明白,這個(gè)正則表達(dá)式的各個(gè)部分都是什么意思,但已經(jīng)知道某些字符具有特殊含義的讀者或許能夠猜出大概了。在這里,「^」和「|」都是正則表達(dá)式的元字符,它們與其他字符結(jié)合起來,實(shí)現(xiàn)我們期望的功能。如果一個(gè)正則表達(dá)式不包括任何egrep支持的元字符,它就成了一個(gè)簡單的“純文本”檢索。例如,在一個(gè)文件中檢索「cat」,會(huì)顯示任何包含c·a·t這3個(gè)連續(xù)字母的行。例如,它包括所有出現(xiàn)了的行。即便這行文本中不包含單詞cat,vacation中包含的c·a·t序列仍然符合匹配條件。如果某行中包含vacation,egrep就會(huì)把它顯示出來。關(guān)鍵就在于,此處進(jìn)行的正則表達(dá)式搜索不是基于“單詞”的——egrep能夠理解文件中的字節(jié)和行,但它完全不理解英語(或者其他任何語言)的單詞、句子、段落,或者是其他復(fù)雜概念。Egrep元字符EgrepMetacharacters現(xiàn)在我們來看egrep中支持正則表達(dá)式功能的元字符。我會(huì)用幾個(gè)例子來簡要介紹它們,把詳細(xì)的例子和描述留到后面的章節(jié)。印刷體例在開始之前,請(qǐng)務(wù)必回顧前言第V頁上解釋的體例說明。本書使用了一些新的文字形式,所以某些體例讀者初次接觸可能并不熟悉。行的起始和結(jié)束StartandEndoftheLine或許最容易理解的元字符就是脫字符號(hào)「^」和美元符號(hào)「$」了,在檢查一行文本時(shí),「^」代表一行的開始,「$」代表結(jié)束。我們?cè)?jīng)看到,正則表達(dá)式「cat」尋找的是一行文本中任意位置的c·a·t,但是「^cat」只尋找行首的c·a·t——「^」用來把匹配文本(這個(gè)表達(dá)式的其他部分匹配的字符)“錨定”(anchor)在這一行的開頭。同樣,「cat$」只尋找位于行末的c·a·t,例如以scat結(jié)尾的行。讀者最好能養(yǎng)成按照字符來理解正則表達(dá)式的習(xí)慣。例如,不要這樣:「^cat」匹配以cat開頭的行而應(yīng)該這樣理解:「^cat」匹配的是以c作為一行的第一個(gè)字符,緊接一個(gè)a,緊接一個(gè)t的文本。這兩種理解的結(jié)果并無差異,但按照字符來解讀更易于明白新遇到的正則表達(dá)式的內(nèi)部邏輯。egrep會(huì)如何解釋「^cat$」、「^$」和單個(gè)的「^」呢??請(qǐng)翻到下頁查看答案。脫字符號(hào)和美元符號(hào)的特別之處就在于,它們匹配的是一個(gè)位置,而不是具體的文本。當(dāng)然,有很多方式可以匹配具體文本。在正則表達(dá)式中,除了使用「cat」之類的普通字符,還可以使用下面幾節(jié)介紹的元字符。字符組CharacterClasses匹配若干字符之一如果我們需要搜索的是單詞“grey”,同時(shí)又不確定它是否寫作“gray”,就可以使用正則表達(dá)式結(jié)構(gòu)體(construct)「[…]」。它容許使用者列出在某處期望匹配的字符,通常被稱作字符組(characterclass(譯注2))?!竐」匹配字符e,「a」匹配字符a,而正則表達(dá)式「[ea]」能匹配a或者e。所以,「gr[ea]y」的意思是:先找到g,跟著是一個(gè)r,然后是一個(gè)a或者e,最后是一個(gè)y。我很不擅長拼寫,所以總是用正則表達(dá)式從一大堆英文單詞中找到正確的拼寫。我經(jīng)常使用的一個(gè)正則表達(dá)式是「sep[ea]r[ea]te」,因?yàn)槲覐膩矶加洸蛔∵@個(gè)單詞到底是寫作“seperate”,“separate”,“separete”,還是別的什么樣子。匹配的結(jié)果的就是正確的拼法,而正則表達(dá)式就是我的領(lǐng)路人。請(qǐng)注意,在字符組以外,普通字符(例如「gr[ae]y」中的「g」和「r」)都有“接下來是(andthen)”的意思——“首先匹配「g」,接下來是「r」……”。這與字符組內(nèi)部的情況是完全相反的。字符組的內(nèi)容是在同一個(gè)位置能夠匹配的若干字符,所以它的意思是“或”。來看另一個(gè)例子,我們還必須考慮單詞的第一個(gè)字母為大寫的情況,例如「[Ss]mith」。請(qǐng)記住,這個(gè)表達(dá)式仍然能夠匹配內(nèi)嵌在其他單詞里頭的smith(或者是Smith),例如blacksmith。在綜述階段,我不打算為這種情況費(fèi)太多筆墨,但是這確實(shí)是某些新手遇到的問題的根源。等了解了更多的元字符以后,我會(huì)介紹一些辦法來解決單詞嵌套的問題。在一個(gè)字符組中可以列舉任意多個(gè)字符。例如「[123456]」匹配1到6中的任意一個(gè)數(shù)字。這個(gè)字符組可以作為「<H[123456]>」的一部分,用來匹配<H1>、<H2>、<H3>等等。在搜索HTML代碼的頭文件時(shí)這非常有用。在字符組內(nèi)部,字符組元字符(character-classmetacharacter)‘-’(連字符)表示一個(gè)范圍:「<H[1-6]>」與「<H[123456]>」是完全一樣的?!竅0-9]」和「[a-z]」是常用的匹配數(shù)字和小寫字母的簡便方式。多重范圍也是容許的,例如「[0123456789abcdefABCDEF]」可以寫作「[0-9a-fA-F]」(或者也可以寫作「[A-Fa-f0-9]」,順序無所謂)。這3個(gè)正則表達(dá)式非常適用于處理十六進(jìn)制數(shù)字。我們還可以隨心所欲地把字符范圍與普通文本結(jié)合起來:「[0-9A-Z_!.?]」能夠匹配一個(gè)數(shù)字、大寫字母、下畫線、驚嘆號(hào)、點(diǎn)號(hào),或者是問號(hào)。請(qǐng)注意,只有在字符組內(nèi)部,連字符才是元字符——否則它就只能匹配普通的連字符號(hào)。其實(shí),即使在字符組內(nèi)部,它也不一定就是元字符。如果連字符出現(xiàn)在字符組的開頭,它表示的就只是一個(gè)普通字符,而不是一個(gè)范圍。同樣的道理,問號(hào)和點(diǎn)號(hào)通常被當(dāng)作元字符處理,但在字符組中則不是如此(說明白一點(diǎn)就是,「[0-9A-Z_!.?]」里面,真正的特殊字符就只有那兩個(gè)連字符)。不妨把字符組看作獨(dú)立的微型語言。在字符組內(nèi)部和外部,關(guān)于元字符的規(guī)定(哪些是元字符,以及它們的意義)是不同的。我們很快就會(huì)看到更多的例子。排除型字符組用「[^…]」取代「[…]」,這個(gè)字符組就會(huì)匹配任何未列出的字符。例如,「[^1-6]」匹配除了1到6以外的任何字符。這個(gè)字符組中開頭的「^」表示“排除(negate)”,所以這里列出的不是希望匹配的字符,而是不希望匹配的字符。讀者可能注意到了,這里的^和第8頁的表示行首的脫字符是一樣的。字符確實(shí)相同,但意義截然不同。英語里的“wind”,根據(jù)情境的不同,可能表示一陣強(qiáng)烈的氣流(風(fēng)),也可能表示給鐘表上發(fā)條;元字符也是如此。我們已經(jīng)看過用來表示范圍的連字符的例子。只有在字符組內(nèi)部(而且不是第一個(gè)字符的情況下),連字符才能表示范圍。在字符組外部,^表示一個(gè)行錨點(diǎn)(lineanchor),但是在字符組內(nèi)部(而且必須是緊接在字符組的第一個(gè)方括號(hào)之后),它就是一個(gè)元字符。請(qǐng)不要擔(dān)心——這就是最復(fù)雜的情況,接下來的內(nèi)容比這簡單。來看另一個(gè)例子,我們需要在一堆英文單詞中搜索出一些特殊的單詞:在這些單詞中,字母q后面的字母不是u。用正則表達(dá)式來表示,就是「q[^u]」。用這個(gè)正則表達(dá)式來搜索我手頭的數(shù)據(jù),確實(shí)得到了一些結(jié)果,但顯然不多,其中還有些是我沒見過的英文單詞。下面是結(jié)果(我輸入的命令用粗體表示):其中有兩個(gè)單詞值得注意:伊拉克“Iraq”和澳大利亞航空公司的名字“Qantas”。盡管它們都在word.list文件中,但都不包含在egrep結(jié)果中。為什么呢??請(qǐng)動(dòng)動(dòng)腦筋,然后翻到下一頁來檢查你的答案。請(qǐng)記住,排除型字符組表示“匹配一個(gè)未列出的字符(matchacharacterthat'snotlisted)”,而不是“不要匹配列出的字符(don'tmatchwhatislisted)”。這兩種說法看起來一樣,但是Iraq的例子說明了其中的細(xì)微差異。有一種簡單的理解排除型字符組的辦法,就是把它們看作普通的字符組,里面包含的是除了“排除型字符組中所有字符”以外的字符。用點(diǎn)號(hào)匹配任意字符MatchingAnyCharacterwithDot元字符「.」(通常稱為點(diǎn)號(hào)dot或者小點(diǎn)point)是用來匹配任意字符的字符組的簡便寫法。如果我們需要在表達(dá)式中使用一個(gè)“匹配任何字符”的占位符(placeholder),用點(diǎn)號(hào)就很方便。例如,如果我們需要搜索03/19/76、03-19-76或者03.19.76,不怕麻煩的話用一個(gè)明確容許‘/’、‘-’、‘.’的字符組來構(gòu)建正則表達(dá)式,例如「03[-./]19[-./]76」。也可以簡單地嘗試「03.19.76」。讀者第一次接觸這個(gè)表達(dá)式時(shí),可能還不清楚某些情況。在「03[-./]19[-./]76」中,點(diǎn)號(hào)并不是元字符,因?yàn)樗鼈冊(cè)谧址M內(nèi)部(記住,在字符組里面和外面,元字符的定義和意義是不一樣的)。這里的連字符同樣也不是元字符,因?yàn)樗鼈兌季o接在[或者[^之后。如果連字符不在字符組的開頭,例如「[.-/]」,就是用來表示范圍的,在本例中就是錯(cuò)誤的用法。在「03.19.76」中,點(diǎn)號(hào)是元字符——它能夠匹配任意字符(包括我們期望的連字符、句號(hào)和斜線)。不過,我們也需要明白,點(diǎn)號(hào)可以匹配任何字符,所以這個(gè)正則表達(dá)式也能夠匹配下面的字符串:‘lotterynumbers:所以,「03[-./]19[-./]76」更加精確,但是更難讀,也更難寫?!?3.19.76」更容易理解,但是不夠細(xì)致。我們應(yīng)該選擇哪一個(gè)呢?這取決于你對(duì)需要檢索的文本的了解,以及你需要達(dá)到的準(zhǔn)確程度。一個(gè)重要但常見的問題是,寫正則表達(dá)式時(shí),我們需要在對(duì)欲檢索文本的了解程度與檢索精確性之間求得平衡。例如,如果我們知道,針對(duì)某個(gè)檢索文本,「03.19.76」這個(gè)正則表達(dá)式基本不可能匹配不期望的結(jié)果,使用它就是合理的。要想正確使用正則表達(dá)式,清楚地了解目標(biāo)文本是非常重要的。多選結(jié)構(gòu)Alternation匹配任意子表達(dá)式「|」是一個(gè)非常簡捷的元字符,它的意思是“或”(or)。依靠它,我們能夠把不同的子表達(dá)式組合成一個(gè)總的表達(dá)式,而這個(gè)總的表達(dá)式又能夠匹配任意的子表達(dá)式。假如「Bob」和「Robert」是兩個(gè)表達(dá)式,但「Bob|Robert」就是能夠同時(shí)匹配其中任意一個(gè)的正則表達(dá)式。在這樣的組合中,子表達(dá)式稱為“多選分支(alternative)”。回頭來看「gr[ea]y」的例子,有意思的是,它還可以寫作「grey|gray」,或者是「gr(a|e)y」。后者用括號(hào)來劃定多選結(jié)構(gòu)的范圍(正常情況下,括號(hào)也是元字符)。請(qǐng)注意,「gr[a|e]y」不符合我們的要求——在這里,‘|’只是一個(gè)和「a」與「e」一樣的普通字符。對(duì)表達(dá)式「gr(a|e)y」來說,括號(hào)是必須的,因?yàn)槿绻麤]有括號(hào),「gra|ey」的意思就成了“「gra」或者「ey」”,而這不符合我們的要求。多選結(jié)構(gòu)可以包括很多字符,但不能超越括號(hào)的界限。另一個(gè)例子是「(First|1st)·[Ss]treet」(注5)。事實(shí)上,因?yàn)椤窮irst」和「1st」都以「st」結(jié)尾,我們可以把這個(gè)結(jié)合體縮略表示為「(Fir|1)st·[Ss]treet」。這樣可能不容易看得清楚,但我們知道「(First|1st)」與「(fir|1)st」表示的是同一個(gè)意思。下面是一些用多選結(jié)構(gòu)來拼寫我名字的例子。這3個(gè)表達(dá)式是一樣的,請(qǐng)仔細(xì)比較:英國拼寫法如下:最后要注意的是,這3個(gè)表達(dá)式其實(shí)與下面這個(gè)更長(但是更簡單)的表達(dá)式是等價(jià)的:「Jeffrey|Geoffery|Jeffery|Geoffrey」。它們只是“殊途同歸”而已。「gr[ea]y」與「gr(a|e)y」的例子可能會(huì)讓人覺得多選結(jié)構(gòu)與字符組沒太大的區(qū)別,但是請(qǐng)留神不要混淆這兩個(gè)概念。一個(gè)字符組只能匹配目標(biāo)文本中的單個(gè)字符,而每個(gè)多選結(jié)構(gòu)自身都可能是完整的正則表達(dá)式,都可以匹配任意長度的文本。字符組基本可以算是一門獨(dú)立的微型語言(例如,對(duì)于元字符,它們有自己的規(guī)定),而多選結(jié)構(gòu)是“正則表達(dá)式語言主體(mainregularexpressionlanguage)”的一部分。你將會(huì)發(fā)現(xiàn),這兩者都非常有用。同樣,在一個(gè)包含多選結(jié)構(gòu)的表達(dá)式中使用脫字符和美元符的時(shí)候也要小心。比較「^From|Subject|Date:·」和「^(From|Subject|Date):·」就會(huì)發(fā)現(xiàn),雖然它們看起來與之前的E-mail的例子很相似,匹配結(jié)果(即它們的用處)卻大不相同。第一個(gè)表達(dá)式由3個(gè)多選分支構(gòu)成,所以它能匹配「^From」或者「Subject」或者「Date:·」,實(shí)用性不大。我們希望在每一個(gè)多選分支之前都有脫字符,之后都有「:·」。所以應(yīng)該使用括號(hào)來“限制”(constrain)這些多選分支:「^(From|Subject|Date):·」現(xiàn)在3個(gè)多選分支都受括號(hào)的限制,所以,這個(gè)正則表達(dá)式的意思是:匹配一行的起始位置,然后匹配「^From」、「Subject」或「Date」中的任意一個(gè),然后匹配「:·」,所以,它能夠匹配的文本是:1)行起始,然后是F·r·o·m,然后是‘:·’,或者2)行起始,然后是S·u·b·j·e·c·t,然后是‘:·’,或者3)行起始,然后是D·a·t·e,然后是‘:·’。簡單點(diǎn)說,就是匹配以‘From:·’,‘Subject:·’或者‘Date:·’開頭的文本行,在提取E-mail文件中的信息時(shí)這很有用。下面是一個(gè)例子:忽略大小寫IgnoringDifferencesinCapitalizationE-mailheader的例子很適合用來說明不區(qū)分大小寫(case-insensitive)的匹配的概念。E-mailheader中的字段類型(fieldtype)通常是以大寫字母開頭的,例如“Subject”和“From”,但是E-mail標(biāo)準(zhǔn)并沒有對(duì)大小寫進(jìn)行嚴(yán)格的規(guī)定,所以“DATE”或者“from”也是合法的字段類型。但是,之前使用的正則表達(dá)式無法處理這種情況。一種辦法是用「[Ff][Rr][Oo][Mm]」取代「From」,這樣就能匹配任何形式的“from”,但缺點(diǎn)之一就是很不方便。幸好,我們有一種辦法告訴egrep在比較時(shí)忽略大小寫,也就是進(jìn)行不區(qū)分大小寫的匹配,這樣就能忽略大小寫字母的差異。該功能并不是正則表達(dá)式語言的一部分,卻是許多工具軟件提供的有用的相關(guān)特性。egrep的命令行參數(shù)“-i”表示進(jìn)行忽略大小寫的匹配。把-i寫在正則表達(dá)式之前:%egrep-i'^(From|Subject|Date):'mailbox結(jié)果除了包括之前的內(nèi)容外,還包含這一行:SUBJECT:MAKEMONEYFAST我使用-i參數(shù)的頻率很高(也許與第12頁的注解有關(guān)),所以我推薦讀者記住它。在下面的章節(jié)中我們還會(huì)見到其他的簡捷特性。單詞分界符WordBoundaries使用正則表達(dá)式時(shí)經(jīng)常會(huì)遇到的一個(gè)問題,期望匹配的“單詞”包含在另一個(gè)單詞之中。在cat、gray和Smith的例子中,我曾提到過這個(gè)問題。不過,某些版本的egrep對(duì)單詞識(shí)別提供了有限的支持:也就是單詞分界符(單詞開頭和結(jié)束的位置)的匹配。如果你的egrep支持“元字符序列(metasequences)”「\<」和「\>」,就可以使用它們來匹配單詞分界的位置??梢园阉鼈兿胂鬄閱卧~版本的「^」和「$」,分別用來匹配單詞的開頭和結(jié)束位置。就像作為行錨點(diǎn)的脫字符和美元符一樣,它們錨定了正則表達(dá)式的其他部分,但在匹配過程中并不對(duì)應(yīng)到任何字符。表達(dá)式「\<cat\>」的意思是“匹配單詞的開頭位置,然后是c·a·t這3個(gè)字母,然后是單詞的結(jié)束位置”。更直接點(diǎn)說就是“匹配cat這個(gè)單詞”。如果讀者愿意,也可以用「\<cat」和「cat\>」來匹配以cat開頭和結(jié)束的單詞。請(qǐng)注意,「<」和「>」本身并不是元字符——只有當(dāng)它們與斜線結(jié)合起來的時(shí)候,整個(gè)序列才具有特殊意義。這就是我稱其為“元字符序列”的原因。重要的是它們的特殊意義,而不是字符的個(gè)數(shù),所以我說的“元字符”和“元(字符)序列”大多數(shù)時(shí)候是等價(jià)的。請(qǐng)記住,并不是所有版本的egrep都支持單詞分界符,即使是支持的版本也不見得聰明到能“認(rèn)得出”英語單詞?!皢卧~的起始位置”只不過是一系列字母和數(shù)字符號(hào)(alphanumericcharacters)開始的位置,而“結(jié)束位置”就是它們結(jié)尾的地方。下一頁的圖1-2說明了一行簡單文本中的單詞分界符。(egrep認(rèn)定的)單詞開頭位置用向上的箭頭標(biāo)識(shí),單詞結(jié)束位置用向下的箭頭標(biāo)識(shí)。我們看到,“單詞的開始和結(jié)束”準(zhǔn)確地說是“字母數(shù)字符號(hào)的開始和結(jié)束”,不過這樣說太麻煩了。圖1-2:“單詞”的起始和結(jié)束位置小結(jié)InaNutshell表1-1總結(jié)了我們已經(jīng)介紹過的元字符。表1-1:至今為止所見的元字符小結(jié)另外還有幾點(diǎn)需要注意:●在字符組內(nèi)部,元字符的定義規(guī)則(及它們的意義)是不一樣的。例如,在字符組外部,點(diǎn)號(hào)是元字符,但是在內(nèi)部則不是如此。相反,連字符只有在字符組內(nèi)部(這是普遍情況)才是元字符,否則就不是。脫字符在字符組外部表示一個(gè)意思,在字符組內(nèi)部緊接著[時(shí)表示另一個(gè)意思,其他情況下又表示別的意思。●不要混淆多選項(xiàng)和字符組。字符組「[abc]」和多選項(xiàng)「(a|b|c)」固然表示同一個(gè)意思,但是這個(gè)例子中的相似性并不能推廣開來。無論列出的字符有多少,字符組只能匹配一個(gè)字符。相反,多選項(xiàng)可以匹配任意長度的文本,每個(gè)多選項(xiàng)可能匹配的文本都是獨(dú)立的,例如「\<(1,000,000|million|thousand·thou)\>」。不過,多選項(xiàng)沒有像字符組那樣的排除功能?!衽懦妥址M是表示所有未列出字符的字符組的簡便方法。因此,「[^x]」的意思并不是“只有當(dāng)這個(gè)位置不是x時(shí)才能匹配”,而是說“匹配一個(gè)不等于x的字符”。其中的差別很細(xì)微,但很重要。例如,前面的概念可以匹配一個(gè)空行,而「[^x]」則不行?!?i參數(shù)規(guī)定在匹配時(shí)不區(qū)分大小寫(?15)(注6)。●目前介紹過的知識(shí)都很有用,但“可選項(xiàng)(optional)”和“計(jì)數(shù)(counting)”元素更重要,下文將馬上介紹??蛇x項(xiàng)元素OptionalItems現(xiàn)在來看color和colour的匹配。它們的區(qū)別在于,后面的單詞比前面的多一個(gè)u,我們可以用「colou?r」來解決這個(gè)問題。元字符「?」(也就是問號(hào))代表可選項(xiàng)。把它加在一個(gè)字符的后面,就表示此處容許出現(xiàn)這個(gè)字符,不過它的出現(xiàn)并非匹配成功的必要條件?!竨?」這個(gè)元字符與我們之前看到的元字符都不相同,它只作用于之前緊鄰的元素。因此,「colou?r」的意思是:「c」,然后是「o」,然后是「l」,然后是「o」,然后是「u?」,最后是「r」。「u?」是必然能夠匹配成功的,有時(shí)它會(huì)匹配一個(gè)u,其他時(shí)候則不匹配任何字符。關(guān)鍵在于,無論u是否出現(xiàn),匹配都是成功的。但這并不等于,任何包含?的正則表達(dá)式都永遠(yuǎn)能匹配成功。例如,「colo」和「u?」都能在‘semicolon’中匹配成功(前者匹配單詞中的colo,后者什么字符都沒有匹配)。可是最后的「r」無法匹配,因此,最終「colou?r」無法匹配semicolon。來看另一個(gè)例子,我們需要匹配表示7月4日(Julyfourth)的文本,其中月份可能寫作July或是Jul,而日期可能寫作fourth、4th或者是4。顯然,我們可以使用「(July|Jul)·(fourth|4th|4)」,但也可以找些其他的辦法來解決這個(gè)問題。首先,我們把「(July|Jul)」縮短為「(July?)」。你明白這種等價(jià)變換嗎?刪除「|」之后,就沒必要保留括號(hào)了。當(dāng)然保留也可以,但不保留括號(hào)顯得更整潔一些。于是我們得到「July?·(fourth|4th|4)」。現(xiàn)在來看第二部分,我們可以把「4th|4」簡化為「4(th)?」。我們看到,現(xiàn)在「?」作用的元素是整個(gè)括號(hào)了。括號(hào)內(nèi)的表達(dá)式可以任意復(fù)雜,但是“從括號(hào)外來看”它們是個(gè)整體。界定「?」的作用對(duì)象(還可以劃定我即將介紹的其他類似元字符的作用對(duì)象)是括號(hào)的主要用途之一。我們的表達(dá)式現(xiàn)在成了「July?·(fourth|4(th)?)」。盡管它包含了許多元字符,而且有嵌套的括號(hào),但理解起來并不困難。我們花了相當(dāng)?shù)墓し騺碇v解這兩個(gè)簡單的例子,但同時(shí)也接觸到了一些相關(guān)的知識(shí),它們相當(dāng)有助于——或許你現(xiàn)在還意識(shí)不到——我們理解正則表達(dá)式。同樣,通過這些講解,我們也積累了依靠不同思路解決問題的經(jīng)驗(yàn)。在閱讀本書(同時(shí)也是在加深理解)尋找復(fù)雜問題的最優(yōu)解決方案的過程中,你可能會(huì)發(fā)現(xiàn)靈感可能在不斷涌現(xiàn)。正則表達(dá)式不是死板的教條,它更像是門藝術(shù)。其他量詞:重復(fù)出現(xiàn)OtherQuantifiers:Repetition「+」(加號(hào))和「*」(星號(hào))的作用與問號(hào)類似。元字符「+」表示“之前緊鄰的元素出現(xiàn)一次或多次”,而「*」表示“之前緊鄰的元素出現(xiàn)任意多次,或者不出現(xiàn)”。換種說法就是,「…*」表示“匹配盡可能多的次數(shù),如果實(shí)在無法匹配,也不要緊”?!浮?」的意思與之類似,也是匹配盡可能多的次數(shù),但如果連一次匹配都無法完成,就報(bào)告失敗。問號(hào)、加號(hào)和星號(hào)這3個(gè)元字符,統(tǒng)稱為量詞(quantifiers),因?yàn)樗鼈兿薅怂饔迷氐钠ヅ浯螖?shù)。與「…?」一樣,正則表達(dá)式中的「…*」也是永遠(yuǎn)不會(huì)匹配失敗的,區(qū)別只在于它們的匹配結(jié)果。而「…+」在無法進(jìn)行任何一次匹配時(shí),會(huì)報(bào)告匹配失敗。舉例來說,「·?」能夠匹配一個(gè)可能出現(xiàn)的空格,但是「·*」能夠匹配任意多個(gè)空格。我們可以用這些量詞來簡化第9頁<H[1-6]>的例子。按照HTML規(guī)范(注7),在tag結(jié)尾的>字符之前,可以出現(xiàn)任意長度的空格,例如<H3·>或者<H4···>。把「·*」加入正則表達(dá)式中的可能出現(xiàn)(但不是必須)空格的位置,就得到「H[1-6]·*」。它仍然能夠匹配<H1>,因?yàn)榭崭癫⒉皇潜仨毘霈F(xiàn)的,但其他形式的tag也能匹配。接下來看類似<HR·SIZE=14>這樣的HTMLtag,它表示一條高度為14像素的穿越屏幕的水平線。與<H3>的例子一樣,在最后的尖括號(hào)之前可以出現(xiàn)任意多個(gè)空格。此外,在等號(hào)兩邊也容許出現(xiàn)任意多個(gè)空格。最后,在HR和SIZE之間必須有至少一個(gè)空格。為了處理更多的空格,我們可以在「·」后添加「·*」,不過最好還是改寫為「·+」。加號(hào)確保至少有一個(gè)空格出現(xiàn),所以它與「··*」是完全等價(jià)的,只不過更簡潔。所以我們得到「<HR·+SIZE·*=·*14·*>」。盡管這個(gè)表達(dá)式不受空格數(shù)目的限制,但它仍然受tag中直線尺寸大小的約束。我們要找的不僅僅是高度為14的tag,而是所有這些tag。所以,我們必須用能匹配普通數(shù)值(generalnumber)的表達(dá)式來替換「14」。在這里,“數(shù)值”(number)是由一位或多位數(shù)字(digits)構(gòu)成的?!竅0-9]」可以匹配一個(gè)數(shù)字,因?yàn)椤爸辽俪霈F(xiàn)一次”,所以我們使用加號(hào)量詞,結(jié)果就是用「[0-9]+」替換「14」。(一個(gè)字符組是一個(gè)“元素”(unit),所以它可以直接加加號(hào)、星號(hào)等,而不需要用括號(hào)。)這樣我們就得到了「<HR·+SIZE·*=·*[0-9]+·*>」,盡管我用了粗體標(biāo)識(shí)元字符,用空格來分隔各個(gè)元素,而且使用了“看得見的空格符”‘·’,這個(gè)表達(dá)式仍然不容易看懂(幸好,egrep提供了-i的參數(shù)?15,這樣我就不需要用「[Hh][Rr]」來表示「HR」了)。否則,「<HR+SIZE*=*[0-9]+*>」更令人迷惑。這個(gè)表達(dá)式之所以看起來有些詭異,是因?yàn)樾翘?hào)和加號(hào)作用的對(duì)象大都是空格,而人眼習(xí)慣于把空格和普通字符區(qū)分開來。在閱讀正則表達(dá)式時(shí),我們必須改變這種習(xí)慣,因?yàn)榭崭穹彩瞧胀ㄗ址?,它與j或者4這樣的字符沒有任何差別(在后面的章節(jié)中,我們會(huì)看到,某些工具軟件支持忽略空格的特殊模式)。我們繼續(xù)這個(gè)例子,如果尺寸這個(gè)屬性也是可選的,也就是說<HR>就代表默認(rèn)高度的直線(同樣,在>之前也可能出現(xiàn)空格)。你能修改我們的正則表達(dá)式,讓它匹配這兩種類型的tag嗎?解決問題的關(guān)鍵在于明白表示尺寸的文本是可選出現(xiàn)的(這是個(gè)暗示)。?請(qǐng)翻到下一頁查看答案。請(qǐng)仔細(xì)觀察最后(答案中)的表達(dá)式,體會(huì)問號(hào)、星號(hào)和加號(hào)之間的差異,以及它們?cè)趯?shí)際應(yīng)用中的真正作用。下一頁的表1-2總結(jié)了它們的意義。請(qǐng)注意,每個(gè)量詞都規(guī)定了匹配成功至少需要的次數(shù)下限,以及嘗試匹配的次數(shù)上限。對(duì)某些量詞來說,下限是0,對(duì)某些量詞來說,上限是無窮大。表1-2:“表示重復(fù)的元字符”含義小結(jié)規(guī)定重現(xiàn)次數(shù)的范圍:區(qū)間某些版本的egrep能夠使用元字符序列來自定義重現(xiàn)次數(shù)的區(qū)間:「…{min,max}」。這稱為“區(qū)間量詞(intervalquantifier)”。例如,「…{3,12}」能夠容許的重現(xiàn)次數(shù)在3到12之間。有人可能會(huì)用「[a-zA-Z]{1,5}」來匹配美國的股票代碼(1到5個(gè)字母)。問號(hào)對(duì)應(yīng)的區(qū)間量詞是{0,1}。支持區(qū)間表示法的egrep的版本并不多,但有許多另外的工具支持它。在第3章我們會(huì)仔細(xì)考察目前經(jīng)常使用的元字符,那時(shí)候會(huì)涉及區(qū)間的支持問題。括號(hào)及反向引用ParenthesesandBackreferences到目前為止,我們已經(jīng)見過括號(hào)的兩種用途:限制多選項(xiàng)的范圍;將若干字符組合為一個(gè)單元,受問號(hào)或星號(hào)之類量詞的作用?,F(xiàn)在我要介紹括號(hào)的另一種用途,雖然它在egrep中并不常見(不過流行的GNU版本確實(shí)支持這一功能),但在其他工具軟件中很常見。在許多流派(flavor)的正則表達(dá)式中,括號(hào)能夠“記住”它們包含的子表達(dá)式匹配的文本。在解決本章開始提到的單詞重復(fù)問題時(shí)就會(huì)用到這個(gè)功能。如果我們確切知道重復(fù)單詞的第一個(gè)單詞(比方說這個(gè)單詞就是“the”),就能夠明確無誤地找到它,例如「the·the」。這樣或許還是會(huì)匹配到的情況,但如果我們的egrep支持在第15頁提到的單詞分界符「\<the·the\>」,這個(gè)問題就很容易解決。我們可以添加「·+」把這個(gè)表達(dá)式變得更靈活。然而,窮舉所有可能出現(xiàn)的重復(fù)單詞顯然是不可能完成的任務(wù)。如果我們先匹配任意一個(gè)單詞,接下來檢查“后面的單詞是否與它一樣”,就好辦多了。如果你的egrep支持“反向引用(backreference)”,就可以這么做。反向引用是正則表達(dá)式的特性之一,它容許我們匹配與表達(dá)式先前部分匹配的同樣的文本。我們先把「\<the·+the\>」中的第一個(gè)「the」替換為能夠匹配任意單詞的正則表達(dá)式「[A-Za-z]+」;然后在兩端加上括號(hào)(原因見下段);最后把后一個(gè)‘the’替換為特殊的元字符序列「\1」,就得到了「\<([A-Za-z]+)·+\1\>」。在支持反向引用的工具軟件中,括號(hào)能夠“記憶”其中的子表達(dá)式匹配的文本,不論這些文本是什么,元字符序列「\1」都能記住它們。當(dāng)然,在一個(gè)表達(dá)式中我們可以使用多個(gè)括號(hào)。再用「\1」、「\2」、「\3」等來表示第一、第二、第三組括號(hào)匹配的文本。括號(hào)是按照開括號(hào)‘(’從左至右的出現(xiàn)順序進(jìn)行的,所以「([a-z])([0-9])\1\2」中的「\1」代表「[a-z]」匹配的內(nèi)容,而「\2」代表「[0-9]」匹配的內(nèi)容。在‘the·the’的例子中,「[A-Za-z]+」匹配第一個(gè)‘the’。因?yàn)檫@個(gè)子表達(dá)式在括號(hào)中,所以「\1」代表的文本就是‘the’。如果「·+」能夠匹配,后面的「\1」要匹配的文本就是‘the’。如果「\1」也能成功匹配,最后的「\>」對(duì)應(yīng)單詞的結(jié)尾(如果文本是‘the·theft’,這一條就不滿足)。如果整個(gè)表達(dá)式能匹配成功,我們就得到一個(gè)重復(fù)單詞。有的重復(fù)單詞并不是錯(cuò)誤,例如‘thatthat’(譯注3),這并不是正則表達(dá)式的錯(cuò)誤,真正的判斷還得靠人。我決定使用上面這個(gè)例子的時(shí)候,已經(jīng)用這個(gè)表達(dá)式檢查過本書之前的內(nèi)容了(我使用的是支持「\<…\>」和反向引用的egrep)。我還使用了第15頁提到的忽略大小寫的參數(shù)-i來拓寬它的適用范圍(注8),所以‘The·the’這樣的單詞重復(fù)也能提取出來。我使用的命令如下:%egrep-i'\<([a-z]+)+\1\>'files…結(jié)果令我驚奇,居然找到了14組重復(fù)單詞。我把它們?nèi)几恼?,而且把這個(gè)表達(dá)式添加到我用來檢查本書拼寫錯(cuò)誤的工具中,保證從此以后全書中不會(huì)出現(xiàn)這樣的錯(cuò)誤。盡管這個(gè)表達(dá)式很有用,我們?nèi)匀恍枰匾曀木窒蕖R驗(yàn)閑grep把每行文字都當(dāng)作一個(gè)獨(dú)立部分來看待,所以如果單詞重復(fù)的第一個(gè)單詞在某行末尾,第二個(gè)單詞在下一行的開頭,這個(gè)表達(dá)式就無法找到。所以,我們需要更加靈活的工具,下一章我們會(huì)看到這方面的例子。神奇的轉(zhuǎn)義TheGreatEscape有個(gè)重要的問題我尚未提及,即:如果需要匹配的某個(gè)字符本身就是元字符,正則表達(dá)式會(huì)如何處理呢?例如,如果我想要檢索互聯(lián)網(wǎng)的主機(jī)名,使用「」可能得到的結(jié)果。還記得嗎?「.」本身就是元字符,它可以匹配任何字符,包括空格。真正匹配文本中點(diǎn)號(hào)的元序列應(yīng)該是反斜線(backslash)加上點(diǎn)號(hào)的組合:「ega\.att\.com」?!竆.」稱為“轉(zhuǎn)義的點(diǎn)號(hào)”或者“轉(zhuǎn)義的句號(hào)”,這樣的辦法適用于所有的元字符,不過在字符組內(nèi)部無效(注9)。這樣使用的反斜線稱為“轉(zhuǎn)義符(escape)”——它作用的元字符會(huì)失去特殊含義,成了普通字符。如果你愿意,也可以把轉(zhuǎn)義符和它之后的元字符看作特殊的元字符序列,這個(gè)元字符序列匹配的是元字符對(duì)應(yīng)的普通字符。這兩種看法是等價(jià)的。我們還可以用「\([a-zA-Z]+\)」來匹配一個(gè)括號(hào)內(nèi)的單詞,例如‘(very)’。在開閉括號(hào)之前的反斜線消除了開閉括號(hào)的特殊意義,于是他們能夠匹配文本中的開閉括號(hào)。如果反斜線后緊跟的不是元字符,反斜線的意義就依程序的版本而定。例如,我們已經(jīng)知道,某些版本的程序把「\<」、「\>」、「\1」當(dāng)作元字符序列對(duì)待。在后面的章節(jié)中我們會(huì)看到更多的例子。基礎(chǔ)知識(shí)拓展ExpandingtheFoundation我希望,前面的例子和解釋已經(jīng)幫助讀者牢固地打下了正則表達(dá)式的基礎(chǔ),也請(qǐng)讀者明白,這些例子都很淺顯,我們需要掌握的還有很多。語言的差異LinguisticDiversification我已經(jīng)介紹過大多數(shù)版本的egrep支持的正則表達(dá)式的特性,這樣的特性還有很多,其中一些并不是所有的版本都支持,這個(gè)問題留到后面的章節(jié)講解。任何語言中都存在不同的方言和口音,很不幸,正則表達(dá)式也一樣。情況似乎是,每一種支持正則表達(dá)式的語言都提供了自己的“改進(jìn)”。正則表達(dá)式不斷發(fā)展,但多年的變化也造就了數(shù)目眾多的正則表達(dá)式“流派”(flavor)。我們會(huì)在下面的章節(jié)中見到各種例子。正則表達(dá)式的目標(biāo)TheGoalofaRegularExpression從最宏觀的角度看,一個(gè)正則表達(dá)式要么能夠匹配給定文本(對(duì)egrep來說,就是一行文本)中的某些字符,要么不能匹配。在編寫正則表達(dá)式的時(shí)候,我們必須進(jìn)行權(quán)衡:匹配符合要求的文本,同時(shí)忽略不符合要求的文本。盡管egrep不關(guān)心匹配文本在行中的位置,但對(duì)正則表達(dá)式的其他應(yīng)用來說,這個(gè)問題卻很重要。如果文本是這樣:…zipis44272.Ifyouwrite,send$4.95tocoverpostageand…我們只希望找出包含「[0-9]+」的那些行,就不需要關(guān)心真正匹配的數(shù)字。相反,如果我們需要操作這些數(shù)字(例如保存到文件、添加、替換之類——我們會(huì)在下一章看到這樣的處理),就需要關(guān)心確切匹配的那些數(shù)字。更多的例子AFewMoreExamples在任何語言中,經(jīng)驗(yàn)都是非常重要的,所以我會(huì)給出更多用正則表達(dá)式匹配常用文本結(jié)構(gòu)的例子。編寫正則表達(dá)式時(shí),按照預(yù)期獲得成功的匹配要花去一半的工夫,另一半的工夫用來考慮如何忽略那些不符合要求的文本。在實(shí)踐中,這兩方面都非常重要,但是目前我們只關(guān)注“獲得成功匹配”的方面。即使我沒有對(duì)這些例子進(jìn)行最全面徹底的解釋,它們?nèi)匀荒軌蛱峁┯杏玫膯⑹?。變量名許多程序設(shè)計(jì)語言都有標(biāo)識(shí)符(identifier,例如變量名)的概念,標(biāo)識(shí)符只包含字母、數(shù)字以及下畫線,但不能以數(shù)字開頭。我們可以用「[a-zA-Z_][a-zA-Z_0-9]*」來匹配標(biāo)識(shí)符。第一個(gè)字符組匹配可能出現(xiàn)的第一個(gè)字符,第二個(gè)(包括對(duì)應(yīng)的「*」)匹配余下的字符。如果標(biāo)識(shí)符的長度有限制,例如最長只能是32個(gè)字符,又能使用第20頁介紹的區(qū)間量詞「{min,max}」,我們可以用「{0,31}」來替代最后的「*」。引號(hào)內(nèi)的字符串匹配引號(hào)內(nèi)的字符串最簡單的辦法是使用這個(gè)表達(dá)式:「"[^"]*"」。兩端的引號(hào)用來匹配字符串開頭和結(jié)尾的引號(hào)。在這兩個(gè)引號(hào)之間的文本可以包括雙引號(hào)之外的任何字符。所以我們用「[^"]」來匹配除雙引號(hào)之外的任何字符,用「*」來表示兩個(gè)引號(hào)之間可以存在任意數(shù)目的非雙引號(hào)字符。關(guān)于引號(hào)字符串,更有用(也更復(fù)雜)的定義是,兩端的雙引號(hào)之間可以出現(xiàn)由反斜線轉(zhuǎn)義的雙引號(hào),例如"nail·the·2\"x4\"·plank"。在后面的章節(jié)講解匹配實(shí)際進(jìn)行的細(xì)節(jié)時(shí),我們會(huì)多次遇到這個(gè)例子。美元金額(可能包含小數(shù))「\$[0-9]+(\.[0-9][0-9])?」是一種匹配美元金額的辦法。從整體上看,這個(gè)表達(dá)式很簡單,分為三部分:「\$」、「…+」和「(…)?」,可以大致理解為:一個(gè)美元符號(hào),然后是一組字符,最后可能還有另一組字符。這里的“字符”指的是數(shù)字(一組數(shù)字構(gòu)成一個(gè)數(shù)值),“另一組字符”是由一個(gè)小數(shù)點(diǎn)和兩位數(shù)字構(gòu)成的。從幾個(gè)方面來看,這個(gè)表達(dá)式還很簡陋。比如,它只能接受$1000,而無法接受$1,000。它確實(shí)能接受可能出現(xiàn)的小數(shù)部分,但對(duì)于egrep來說意義不大。因?yàn)閑grep從不關(guān)心匹配文字的內(nèi)容,而只關(guān)心是否存在匹配。處理可能出現(xiàn)的小數(shù)部分對(duì)整個(gè)表達(dá)式能否匹配并沒有影響。但是,如果我們需要找到只包含價(jià)格而不含其他字符的行,倒是可以在這個(gè)表達(dá)式兩端加上「^…$」。這樣一來,可選的小數(shù)部分就變得很重要了,因?yàn)樵诮痤~數(shù)值和換行符之間是否存在小數(shù)部分,決定了整個(gè)表達(dá)式的匹配結(jié)果是否存在差異。另外,這個(gè)正則表達(dá)式還無法匹配‘$.49’。你可能認(rèn)為把加號(hào)換成星號(hào)能夠解決問題,不過這條路走不通。在這我先賣個(gè)關(guān)子,答案留待第5章(?194)揭曉。HTTP/HTMLURLWebURL的形式可能有很多種,所以構(gòu)造一個(gè)能夠匹配所有形式的URL的正則表達(dá)式頗有難度。不過,稍微降低一點(diǎn)要求的話,我們能夠用一個(gè)相當(dāng)簡單的正則表達(dá)式來匹配大多數(shù)常見的URL。進(jìn)行這種檢索的原因之一是,我只能大概記得在收到的某封郵件中有一個(gè)URL地址,不過一見到它我就能認(rèn)出來。常見的HTTP/HTMLURL是下面這樣的:http://hostname/path.html當(dāng)然,.htm的結(jié)尾也很常見。hostname(主機(jī)名,例如)的規(guī)則比較復(fù)雜,但是我們知道,跟在‘http://’之后的就有可能是主機(jī)名,所以這個(gè)正則表達(dá)式就很簡單,「[-a-z0-9_.]+」。path部分的變化更多,所以我們需要使用「[-a-z0-9_:@&?=+,.!/~*%$]*」。請(qǐng)注意,連字符必須放在字符組的開頭,保證它是一個(gè)普通字符,而不是用來表示范圍(?9)。綜合起來,我們第一次嘗試的正則表達(dá)式就是:%egrep-i'\<http://[-a-z0-9_.:]+/[-a-z0-9_:@&?=+,.!/~*%$]*\.html?\>'files因?yàn)槲覀兘档土藢?duì)匹配的要求,所以‘http:///foo.html’也能匹配,雖然它顯然不是一個(gè)合法的URL。我們需要關(guān)心這一點(diǎn)嗎?這取決于具體的情況。如果我只是需要掃描自己的E-mail,得到一些錯(cuò)誤結(jié)果并不算是問題。而且,我沒準(zhǔn)會(huì)用更簡單的表達(dá)式:%egrep-i'\<http://[^]*\.html?\>'files…在深入了解如何調(diào)校正則表達(dá)式之后,讀者會(huì)明白,要想在復(fù)雜性和完整性之間求得平衡,一個(gè)重要的因素是了解待搜索的文本。下一章,我們會(huì)更詳細(xì)地考察這個(gè)例子。HTMLtag對(duì)egrep這樣的工具來說,簡單地匹配包含HTMLtag的行并不常見,也沒什么用。但是,探索如何準(zhǔn)確匹配一個(gè)HTMLtag卻是相當(dāng)有啟發(fā)的,在下一章深入接觸更高級(jí)的工具時(shí),這一點(diǎn)尤其明顯。簡單的例子包括‘<TITLE>’和‘<HR>’,我們可能會(huì)想到「<.*>」。這個(gè)簡單的表達(dá)式往往是最直接的想法,但它顯然是不對(duì)的?!福?*>」的意思是,“先匹配一個(gè)‘<’,然后是任意多個(gè)任意字符,然后是‘>’”。所以,它無疑能夠匹配不止一個(gè)tag的內(nèi)容,例如‘thisexample’中標(biāo)記的內(nèi)容。也許結(jié)果有點(diǎn)出乎你的意料,但是我們目前還只在第1章,對(duì)正則表達(dá)式的理解也不夠深入。我之所以舉這個(gè)例子,是想說明正則表達(dá)式并不復(fù)雜,但是如果你不真正弄懂它們,可能會(huì)被搞得暈頭轉(zhuǎn)向。在下面的幾章中,我們會(huì)學(xué)習(xí)理解和解決這個(gè)問題需要的所有細(xì)節(jié)。表示時(shí)刻的文字,例如“9:17am”或者“12:30pm”匹配表示時(shí)刻的文字可能有不同的嚴(yán)格程度?!竅0-9]?[0-9]:[0-9][0-9]·(am|pm)」能夠匹配9:17·am或者12:30·pm,但也能匹配無意義的時(shí)刻,如99:99·pm。首先看小時(shí)數(shù),我們知道,如果小時(shí)數(shù)是一個(gè)兩位數(shù),第一位只能是1。但是「1?[0-9]」仍然能夠匹配19(也能夠匹配0),所以更好的辦法應(yīng)該是把小時(shí)部分分為兩種情況來處理,「1[012]」匹配兩位數(shù),「[1-9]」匹配一位數(shù),結(jié)果就是「(1[012]|[1-9])」。分鐘數(shù)就簡單些。第一位數(shù)字應(yīng)該是「[0-5]」,此時(shí)第二位數(shù)字應(yīng)該是「[0-9]」。綜合起來就是「(1[012]|[1-9]):[0-5][0-9]·(am|pm)」。舉一反三,你能夠處理24小時(shí)制的時(shí)間嗎?多動(dòng)動(dòng)腦筋,想想該如何處理以0開頭的情況,比如09:59呢??答案請(qǐng)見下頁。正則表達(dá)式術(shù)語匯總RegularExpressionNomenclature正則(regex)你或許已經(jīng)猜到了,“正則表達(dá)式”(regularexpression)這個(gè)全名念起來有點(diǎn)麻煩,寫出來就更麻煩。所以,我一般會(huì)采用“正則”(regex)的說法。這個(gè)單詞念起來很流暢(有點(diǎn)像聯(lián)邦快遞的FedEx,與regular一樣,g發(fā)重音,而不同于Regina),而且說“如果你寫一個(gè)正則”,“巧妙的正則”(buddingregexers),甚至是“正則化”(regexification)(注10)(譯注4)。匹配(matching)一個(gè)正則表達(dá)式“匹配”一個(gè)字符串,其實(shí)是指這個(gè)正則表達(dá)式能在字符串中找到匹配文本。嚴(yán)格地說,正則表達(dá)式「a」不能匹配cat,但是能匹配cat中的a。幾乎沒人會(huì)混淆這兩個(gè)概念,但澄清一下還是有必要的。元字符(metacharacter)一個(gè)字符是否元字符(或者是“元字符序列”(metasequence),這兩個(gè)概念是相等的),取決于應(yīng)用的具體情況。例如,只有在字符組外部并且是在未轉(zhuǎn)義的情況下,「*」才是一個(gè)元字符?!稗D(zhuǎn)義”(escaped)的意思是,通常情況下在這個(gè)字符之前有一個(gè)反斜線?!竆*」是對(duì)「*」的轉(zhuǎn)義,而「\\*」則不是(第一個(gè)反斜線用來轉(zhuǎn)義第二個(gè)反斜線),雖然在兩個(gè)例子中,星號(hào)之前都有一個(gè)反斜線。正則表達(dá)式的流派(flavor)不同,關(guān)于字符轉(zhuǎn)義的規(guī)定也不相同。第3章對(duì)此進(jìn)行了詳細(xì)討論。流派(flavor)我已經(jīng)說過,不同的工具使用不同的正則表達(dá)式完成不同的任務(wù),每樣工具支持的元字符和其他特性各有不同。我們?cè)倥e單詞分界符的例子。某些版本的egrep支持我們?cè)娺^的\<…\>表示法。而另一些版本不支持單獨(dú)的起始和結(jié)束邊界,只提供了統(tǒng)一的「\b」元字符(這個(gè)元字符我們還沒見過,下一章才會(huì)用到)。還有些工具同時(shí)支持這兩種表示法,另有許多工具哪種也不支持。我用“流派(flavor)”這個(gè)詞來描述所有這些細(xì)微的實(shí)現(xiàn)規(guī)定。這就好像不同的人說不同的方言一樣。從表面上看,“流派”指的是關(guān)于元字符的規(guī)定,但它的內(nèi)容遠(yuǎn)遠(yuǎn)不止這些。即使兩個(gè)程序都支持「\<…\>」,它們可能對(duì)這兩個(gè)元字符的意義有不同的理解,對(duì)單詞的理解也不相同。在使用具體的工具軟件時(shí),這個(gè)問題尤其重要。請(qǐng)不要混淆“流派(flavor)”和“工具(tool)”這兩個(gè)概念。兩個(gè)人可以說同樣的方言,兩個(gè)完全不同的程序也可能屬于同樣的流派。同樣,兩個(gè)名字相同的程序(解決的任務(wù)也相同)所屬的流派可能有細(xì)微(有時(shí)可能并非細(xì)微)的差別。有許多程序都叫egrep,它們所屬的流派也五花八門。由Perl語言的正則表達(dá)式開創(chuàng)的流派,在20世紀(jì)90年代中期因?yàn)槠鋸?qiáng)大的表達(dá)能力廣為人們所知,其他語言緊隨其后,提供了汲取其中靈感的正則表達(dá)式(其中許多為了標(biāo)明自己的思想來源,直接給自己貼上“兼容Perl(Perl-Compatible)”的標(biāo)簽)。它們包括PHP、Python、Java的大量正則包,微軟的.NETFramework、Tcl,以及C的各種類庫。不過,所有這些語言在重要的方面各有不同。而且Perl的正則表達(dá)式也在不斷演化和發(fā)展(現(xiàn)在,有時(shí)候是受了其他語言的正則表達(dá)式的刺激)。像往常一樣,總的局面變得越來越復(fù)雜,讓人困惑。子表達(dá)式(subexpression)“子表達(dá)式”指的是整個(gè)正則表達(dá)式中的一部分,通常是括號(hào)內(nèi)的表達(dá)式,或者是由「|」分隔的多選分支。例如,在「^(Subject|Date):·」中,「Subject|Date」通常被視為一個(gè)子表達(dá)式。其中的「Subject」和「Date」也算得上子表達(dá)式。而且,嚴(yán)格說起來,「S」、「u」、「b」、「j」這些字符,都算子表達(dá)式。1-6這樣的字符序列并不能算「H[1-6]·*」的子表達(dá)式,因?yàn)椤?-6’所屬的字符組是不可分割的“單元(unit)”。但是,「H」、「[1-6]」、「·

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論