摘 要:在編寫處理字符串的程序或網(wǎng)頁時,經(jīng)常會有查找符合某些復雜規(guī)則的字符串的需要。正則表達式就是用于描述這些規(guī)則的語法。本文通過與、或、非這三種邏輯運算來闡述正則表達式的正確使用方法。
關(guān)鍵詞:正則表達式;與;或;非
中圖分類號:TP393.08
正則表達式是對字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個“規(guī)則字符串”,這個“規(guī)則字符串”用來表達對字符串的一種過濾邏輯。在計算機科學中,是指一個用來描述或者匹配一系列符合某個句法規(guī)則的字符串的單個字符串。我們可以歸納為三種邏輯,即與、或、非。
一般來說,正則表達式千變?nèi)f化,總是這三種邏輯的組合。比如匹配雙引號字符串\"quoted string\"。
與:首尾的雙引號字符必須出現(xiàn)。
或:兩個雙引號之間的字符個數(shù)是不確定的(如果是空字符串””,則兩個雙引號之間沒有字符)。
非:兩個雙引號之間不能出現(xiàn)雙引號字符。
再比如匹配html中的open-tag(比如
與:首尾必須分別是<和>,如果是close-tag,則<之后必須出現(xiàn)/。
或:<和>之間必須出現(xiàn)至少一個字符(<>不是一個合法的tag)。
非:<之后不能是/字符,如果是open-tag,<之后不能出現(xiàn)/。
下面我們來解析三種邏輯的對策。
1 與
“與”是正則表達式中最普通的邏輯關(guān)系。一般來說,如果正則表達式中的元素沒有任何量詞(quantifier,比如*、?、+)修飾,就是“與”關(guān)系。比如『<』,就表示“這里必須出現(xiàn)<字符”;『cat』,就表示“這里必須依次出現(xiàn)c、a、t,3個字符”,但有時“必須出現(xiàn)”的是若干個元素,但它們之間并不相連。
2 或
“或”是正則表達式中最靈活的邏輯關(guān)系。正則表達式能應對各種不同的文本,“或”功能不可或缺。
如果“或”的意思是,元素可以出現(xiàn),也可以不出現(xiàn),或者出現(xiàn)的次數(shù)不確定,可以用量詞來表示“或”關(guān)系。比如表達式『a?』,表示在此處,字符a可以出現(xiàn),也可以不出現(xiàn);表達式『(ab)+』,表示在此處,字符串a(chǎn)b必然要出現(xiàn)1次,也可以出現(xiàn)無限多次。
如果“或”的意思是,可以出現(xiàn)的是某幾個元素中的一個,則應該使用字符組或者多選結(jié)構(gòu)。當元素都是單個字符時,就應該使用字符組『[…]』:比如匹配單詞cat或者cut,除去開頭的a、結(jié)尾的t是固定的,之中“或許出現(xiàn)a,或許出現(xiàn)u”,所以應當使用字符組『[au]』,整個正則表達式就是『c[au]t』。當元素不只單個字符(只要有一個元素不只單個字符)時,就應該使用多選結(jié)構(gòu)『(…|…)』:比如不但要匹配單詞cat或者cut,還要匹配單詞chart、conduct和court,除去開頭的a、結(jié)尾的t是固定的,之中“或許出現(xiàn)a,或許出現(xiàn)u,或許出現(xiàn)har,或許出現(xiàn)onduc,或許出現(xiàn)our”,這時候就應該使用多選結(jié)構(gòu)『(a|u|har|onduc|our)』,整個正則表達式就是『c(a|u|har|onduc|our)t』。
當然,多選分支也可以表示字符組,比如『[au]』就可以表示為『(a|u)』,兩者的功能是完全等價的。
在實踐中,“與”和“或”經(jīng)常同時出現(xiàn),比如這個URL pattern:/foo/bar_tmp.php。foo是模塊名,bar是控制器名,tmp是方法名。合法的URL并不要求3個名字每次都出現(xiàn),可以只出現(xiàn)控制器名(/foo),也可以只出現(xiàn)控制器名和模塊名(/foo/bar.php),也可以3者都出現(xiàn)(/foo/bar_tmp.php)。
這里的模塊名、控制器名、方法名,都可以用『[a-z]+』匹配,我們暫用foo、bar、tmp代替對應的表達式。
與:/foo必須出現(xiàn)。
或:/bar和.php是可選出現(xiàn)的,但必須同時出現(xiàn),或同時不出現(xiàn)(與)/foo必須出現(xiàn),這很好表示,暫且不去管它;/bar和.php如果出現(xiàn),必須同時出現(xiàn),所以它們應該作為一個元素,寫作『(/bar.php)』;整個元素可選出現(xiàn),所以給它添加量詞,得到『(/bar.php)?』;最后,在/bar和.php都出現(xiàn)的前提下,_tmp才可以出現(xiàn),所以將『(_tmp)?』填充到『(/bar.php)?』,得到『(/bar(_tmp)?.php)?』,最后加上開頭的/foo,整個表達式就是『(/bar(_tmp)?.php)?』。
3 非
“非”是正則表達式中最難處理的邏輯關(guān)系。因為沒有直接對應的結(jié)構(gòu)。最簡單的“非”,意思是此處不能出現(xiàn)某個字符,似乎用排除型字符組『[^…]』就可以解決。我們?nèi)匀慌ecat和cut的例子,如果仍然希望匹配c開頭、t結(jié)尾的單詞,但不希望匹配cut,可以寫成『c[^u]t』,即:最開頭的字母是c,之后是一個不為u的字符,之后是t。沒錯,它確實不會匹配cut,也可以匹配cat。但是,chart、conduct、court等等,它也沒法匹配,因為[^u]的意思是:匹配一個不是u的字符。
分析要實現(xiàn)的功能:
與:以c開頭,以t結(jié)尾。
或:c和t之間可以出現(xiàn)的字母必須多于一個,沒有上限。
非:c和t之間不能只有一個字符u。
在c之間的位置向后看,不能出現(xiàn)cut。這一點,正好對應否定順序環(huán)視(positive look-ahead)功能,『(?!cut)』就是用來進行這種判斷的,它判斷之后的字符串能不能由cut匹配,但并不真正真正進行匹配,也不會移動“當前位置”。所以我們將它放在表達式的最開頭,得到『(?!cut)c[a-z]+t』。這個表達式的邏輯是:只有在當前位置右側(cè)字符串不能由cut匹配的情況下,才從這里開始,向右嘗試用c[a-z]+t。
如果我們更進一步,需要排除掉cat和cut,可以把否定順序環(huán)視改為『(?!c[au]t)』。這樣就能保證,匹配到的肯定不是cat或者cut。
更復雜一點,如果我們要驗證這樣一個字符串:它全部由小寫字母構(gòu)成,長度不超過12位,其中不能包含unfavored或者unwanted。也可以照章處理,先匹配“長度不超過12位”的小寫字母『[a-z]{,12}』,然后寫出匹配“不需要匹配內(nèi)容”的正則表達式,『(unfavored|unwanted)』,再用否定順序環(huán)視將它“排除”即可,只是這次要注意,不能直接寫『(??。╱nfavored|unwanted))』,因為它只能排除『(unfavored|unwanted)』出現(xiàn)在字符串開頭的情況,為了排除它出現(xiàn)在字符串中的情況,我們要把否定順序環(huán)視改為『(?![a-z]*(unfavored|unwanted))』,這樣就確保完整的“排除”,整個表達式就是『(?![a-z]*(unfavored|unwanted))[a-z]{,12}』。
正則表達式中的“非”,除去能用排除型字符組直接表示的,復雜一點的“非”邏輯都是按照這樣的思路進行的:先用一個正則表達式準確匹配需要“排除”的字符串,再用環(huán)視功能排除掉它——“非”確實是正則表達式中,最難處理的邏輯關(guān)系,好在它并不復雜,而且,除去一些比較古老的工具(比如Apache 1.3),現(xiàn)在各種工具和語言,基本都支持這種功能。
參考文獻:
[1]張樹壯,羅浩,方濱興.面向網(wǎng)絡(luò)安全的正則表達式匹配技術(shù)[J].軟件學報,2011(08).
[2]鐘京馗.JAVA中的正則表達式及其應用[J].電腦編程技巧與維護,2005(06).
[3]杜冬梅,許彩欣,蘇健.淺談?wù)齽t表達式在web系統(tǒng)中的應用[J].計算機系統(tǒng)應用,2007(08).
[4]鄧緒斌,朱揚勇.ReDE:一個基于正則表達式的生物數(shù)據(jù)抽取方法[J].計算機研究與發(fā)展,2005(12).
作者簡介:周興旺(1979.11-),男,揚州人,講師,碩士,研究方向:計算機網(wǎng)絡(luò)技術(shù)、數(shù)據(jù)挖掘、教育教學管理等。
作者單位:南通農(nóng)業(yè)職業(yè)技術(shù)學院,江蘇南通 226007
基金項目:江蘇高校哲學社會科學研究項目(項目編號:NO.2014SJD641)。