PHP擴(kuò)展之文本處理(二)——PCRE正則表達(dá)式語(yǔ)法9——重復(fù)/量詞
重復(fù)次數(shù)是通過(guò)量詞指定的,可以緊跟在下面元素之后:
單獨(dú)的字符, 可以是經(jīng)過(guò)轉(zhuǎn)義的元字符。字符類后向引用(參加下一部分)子組(除非它是一個(gè)斷言)一般的重復(fù)量詞指定了一個(gè)最小數(shù)值和一個(gè)最大數(shù)值的匹配次數(shù), 通過(guò)花括號(hào)包裹兩個(gè)數(shù)字,兩個(gè)數(shù)字之間用逗號(hào)隔開的語(yǔ)法定義。 兩個(gè)數(shù)值都必須小于 65536, 并且第一個(gè)數(shù)字必須小于等于第二個(gè)。 比如:?z{2,4}?匹配 ”zz”, “zzz”, “zzzz”。 單個(gè)的右花括號(hào)不是特殊字符。 如果第二個(gè)數(shù)字被省略,但是逗號(hào)仍然存在,就代表沒(méi)有上限; 如果第二個(gè)數(shù)字和逗號(hào)都被省略,那么這個(gè)量詞就限定的是一個(gè)確定次數(shù)的匹配。 比如?[aeiou]{3,}?匹配至少三個(gè)連續(xù)的元音字母,但是同時(shí)也可以匹配更多, 而?d{8}?則只能匹配 8 個(gè)數(shù)字。 左花括號(hào)出現(xiàn)在不允許使用量詞的位置或者與量詞語(yǔ)法不匹配時(shí), 被認(rèn)為是一個(gè)普通字符,對(duì)它自身進(jìn)行原文匹配。 比如,{,6}就不是一個(gè)量詞, 會(huì)按照原文匹配四個(gè)字符 ”{,6}”。
量詞 {0} 是被授權(quán)的,它會(huì)導(dǎo)致的行為是認(rèn)為前面的項(xiàng)和量詞不存在。
為了方便(以及歷史的兼容性),最常用的三個(gè)量詞都有單字符縮寫。
單字符量詞*等價(jià)于?{0,}+等價(jià)于?{1,}?等價(jià)于?{0,1}可以通過(guò)一個(gè)不匹配任何字符的子模式后面緊跟一個(gè)匹配 0 或多個(gè)字符的量詞來(lái)構(gòu)造一個(gè)沒(méi)有上限的無(wú)限循環(huán)。 比如:(a?)*
早期版本的 perl 和 pcre 對(duì)于這種模式會(huì)在編譯期得到一個(gè)錯(cuò)誤。然而, 由于這在某些情況下是有用的,因此現(xiàn)在也接受這種模式了, 但是如果任何子模式的重復(fù)確實(shí)匹配不到任何字符,循環(huán)會(huì)被強(qiáng)制跳出。
默認(rèn)情況下,量詞都是”貪婪”的,也就是說(shuō), 它們會(huì)在不導(dǎo)致模式匹配失敗的前提下,盡可能多的匹配字符(直到最大允許的匹配次數(shù))。 這種問(wèn)題的典型示例就是嘗試匹配C語(yǔ)言的注釋。 出現(xiàn)在 /* 和 */ 之間的所有內(nèi)容都被認(rèn)為是注釋, 在注釋中間, 可以允許出現(xiàn)單獨(dú)的 * 和 /。 對(duì)C注釋匹配的一個(gè)嘗試是使用模式?/*.**/, 假設(shè)將此模式應(yīng)用在字符串 ”/* first comment*/ not comment /*second comment*/” 它會(huì)匹配到錯(cuò)誤的結(jié)果,也就是整個(gè)字符串, 這是因?yàn)榱吭~的貪婪性導(dǎo)致的,它會(huì)嘗試盡可能多的匹配字符。
然而,如果一個(gè)量詞緊跟著一個(gè) ?(問(wèn)號(hào)) 標(biāo)記,它就會(huì)成為懶惰(非貪婪)模式, 它不再盡可能多的匹配,而是盡可能少的匹配。 因此模式?/*.*?*/?在 C 的注釋匹配上將會(huì)正確的執(zhí)行。 各個(gè)量詞自身的意義并不會(huì)改變,而是由于加入了 ? 使其首選的匹配次數(shù)發(fā)生改變。 不要將 ? 的這個(gè)用法和它作為量詞的用法混淆。因?yàn)樗謨煞N用法, 因此有時(shí)它會(huì)出現(xiàn)量詞,比如?d??d?會(huì)更傾向于匹配一個(gè)數(shù)字, 但同時(shí)如果為了達(dá)到整個(gè)模式匹配的目的,它也可以接受兩個(gè)數(shù)字的匹配。譯注: 以模式 wd??dw 為例,對(duì)于字符串 ”a33a”,雖然 d?? 是非貪婪的, 但由于如果使用貪婪會(huì)導(dǎo)致整個(gè)模式不匹配,所以, 最終它選擇的仍然是匹配到一個(gè)數(shù)字。
如果?PCRE_UNGREEDY?選項(xiàng)被設(shè)置(一個(gè)在 perl 中不可用的選項(xiàng)), 那么量詞默認(rèn)情況下就是非貪婪的了。但是, 單個(gè)的量詞可以通過(guò)緊跟一個(gè) ? 來(lái)使其成為貪婪的。換句話說(shuō), PCRE_UNGREEDY 這個(gè)選項(xiàng)逆轉(zhuǎn)了貪婪的默認(rèn)行為。
量詞后面緊跟一個(gè) ”+” 是”占有”性。它會(huì)吃掉盡可能多的字符, 并且不關(guān)注后面的其他模式,比如?.*abc?匹配 ”aabc”, 但是?.*+abc?不會(huì)匹配, 因?yàn)?.*+?會(huì)吃掉整個(gè)字符串,從而導(dǎo)致后面剩余的模式得不到匹配。 自PHP 4.3.3 起, 可以使用占有符 (+) 修飾量詞來(lái)達(dá)到提升速度的目的。
當(dāng)一個(gè)子組受最小數(shù)量大于 1 或有一個(gè)最大數(shù)量限制的量詞修飾時(shí), 按照最小或最大的數(shù)量的比例需要更多的存儲(chǔ)用于編譯模式。
如果一個(gè)模式以 .* 或 .{0,} 開始并且?PCRE_DOTALL?選項(xiàng)開啟(等價(jià)于 perl 的/s), 也就是允許.匹配換行符,那么模式會(huì)隱式的緊固,因?yàn)椴还茉趺礃樱?接下來(lái)都會(huì)對(duì)目標(biāo)字符串中的每個(gè)字符位置進(jìn)行嘗試,因此在第一次之后, 在任何位置都不會(huì)有一個(gè)對(duì)所有匹配重試的點(diǎn)。 PCRE 會(huì)想對(duì)待 A 一樣處理這個(gè)模式。 在我們已知目標(biāo)字符串沒(méi)有包含換行符的情況下, 當(dāng)模式以 .* 開始的時(shí)候我們?yōu)榱双@得這個(gè)優(yōu)化,值得設(shè)置?PCRE_DOTALL, 或者選擇使用 ^ 明確指明錨定。
譯注:這里的優(yōu)化指模式不匹配之后,不會(huì)回頭再來(lái)查找下一個(gè)位置, 比如沒(méi)有設(shè)置 PCRE_DOTALL,并且目標(biāo)字符串第一個(gè)字符時(shí)換行符, 那么模式嘗試第一個(gè)字符,發(fā)現(xiàn)不匹配, 會(huì)重新用模式從第二個(gè)字符位置開始進(jìn)行嘗試。 而使用了PCRE_DOTALL后, 是肯定匹配的….同理,當(dāng)使用了 ^ 或者 /A的限定是,模式一旦不匹配,都可以直接退出, 而不用在目標(biāo)字符串下一個(gè)位置再一次開始整個(gè)模式的匹配。
當(dāng)一個(gè)捕獲子組時(shí)重復(fù)的時(shí),捕獲到的該子組的結(jié)果是最后一次迭代捕獲的值。比如,?(tweedle[dume]{3}s*)+匹配字符串 ”tweedledum tweedledee”, 得到的的子組捕獲結(jié)果是 ”tweedledee”。然而,如果是嵌套的捕獲子組, 相應(yīng)的捕獲值可能會(huì)被設(shè)置到之前的迭代中。比如,/(a|(b))+/?匹配字符串 ”aba”, 第二個(gè)捕獲子組得到的結(jié)果會(huì)是 ”b”。譯注:不理解然而之后的部分,以例子說(shuō)明, b 是第二個(gè)子組最后一次捕獲到的結(jié)果,所以, 第二個(gè)子組最后結(jié)果是 b, 這是符合”然而”之前描述的規(guī)則的。
相關(guān)文章:
1. JSP數(shù)據(jù)交互實(shí)現(xiàn)過(guò)程解析2. 解決啟動(dòng)django,瀏覽器顯示“服務(wù)器拒絕訪問(wèn)”的問(wèn)題3. Nginx+php配置文件及原理解析4. vue使用webSocket更新實(shí)時(shí)天氣的方法5. Yii2.0引入CSS,JS文件方法6. Opencv+Python識(shí)別PCB板圖片的步驟7. ASP.NET MVC獲取多級(jí)類別組合下的產(chǎn)品8. python使用selenium爬蟲知乎的方法示例9. 討論CSS中的各類居中方式10. 如何使用CSS3畫出一個(gè)叮當(dāng)貓
