這篇文章主要介紹了正則表達(dá)式如何實(shí)現(xiàn)逆序環(huán)視,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
石城ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書(shū)銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書(shū)合作)期待與您的合作!
1 問(wèn)題引出
前幾天在CSDN論壇遇到這樣一個(gè)問(wèn)題。
我要通過(guò)正則分別取出下面 與 之間的字符串
1、在 與 之間的字符串是沒(méi)法固定的,是隨機(jī)自動(dòng)生成的
2、其中 與 的數(shù)量也是沒(méi)法固定的,也是隨機(jī)自動(dòng)生成的
** 這里是不固定的字符串1 **
** 這里是不固定的字符串2 **
** 這里是不固定的字符串3 **
有朋友給出這樣的正則“(?<=)([\s\S]*?)(?=)”,看下匹配結(jié)果。
代碼如下:
string test = @" ** 這里是不固定的字符串1 **
** 這里是不固定的字符串2 **
** 這里是不固定的字符串3 ** ";
MatchCollection mc = Regex.Matches(test, @"(?<=)([\s\S]*?)(?=)");
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n---------------\n";
}
/*--------輸出--------
** 這里是不固定的字符串1 **
---------------
** 這里是不固定的字符串2 **
---------------
** 這里是不固定的字符串3 **
---------------
*/
為什么會(huì)是這樣的結(jié)果,而不是我們期望的如下的結(jié)果呢?
/*--------輸出--------
** 這里是不固定的字符串1 **
---------------
** 這里是不固定的字符串2 **
---------------
** 這里是不固定的字符串3 **
---------------
*/
這涉及到逆序環(huán)視的匹配原理,以及貪婪與非貪婪模式應(yīng)用的一些細(xì)節(jié),下面先針對(duì)逆序環(huán)視的匹配細(xì)節(jié)展開(kāi)討論,然后再回過(guò)頭來(lái)看下這個(gè)問(wèn)題。
2 逆序環(huán)視匹配原理
關(guān)于環(huán)視的一些基礎(chǔ)講解和基本匹配原理,在正則基礎(chǔ)之——環(huán)視這篇博客里已有所介紹,只不過(guò)當(dāng)時(shí)整理得比較匆忙,沒(méi)有涉及更詳細(xì)的匹配細(xì)節(jié)。這里僅針對(duì)逆序環(huán)視展開(kāi)討論。
逆序環(huán)視的基礎(chǔ)知識(shí)在上面博文中已介紹過(guò),這里簡(jiǎn)單引用一下。
表達(dá)式 | 說(shuō)明 |
(?<=Expression) | 逆序肯定環(huán)視,表示所在位置左側(cè)能夠匹配Expression |
(? | 逆序否定環(huán)視,表示所在位置左側(cè)不能匹配Expression |
對(duì)于逆序肯定環(huán)視(?<=Expression)來(lái)說(shuō),當(dāng)子表達(dá)式Expression匹配成功時(shí),(?<=Expression)匹配成功,并報(bào)告(?<=Expression)匹配當(dāng)前位置成功。
對(duì)于逆序否定環(huán)視(?
2.1 逆序環(huán)視匹配行為分析
2.1.1 逆序環(huán)視支持現(xiàn)狀
目前支持逆序環(huán)視的語(yǔ)言還比較少,比如當(dāng)前比較流行的腳本語(yǔ)言JavaScript中就是不支持逆序環(huán)視的。個(gè)人認(rèn)為不支持逆序環(huán)視已成為目前JavaScript中使用正則的最大限制,一些使用逆序環(huán)視很輕松搞定的輸入驗(yàn)證,卻要通過(guò)各種變通的方式來(lái)實(shí)現(xiàn)。
需求:驗(yàn)證輸入由字母、數(shù)字和下劃線組成,下劃線不能出現(xiàn)在開(kāi)始或結(jié)束位置。
對(duì)于這樣的需求,如果支持逆序環(huán)視,直接“^(?!_)[a-zA-Z0-9_]+(?
而另一些流行的語(yǔ)言,比如Java中,雖然支持逆序環(huán)視,但只支持固定長(zhǎng)度的子表達(dá)式,量詞也只支持“?”,其它不定長(zhǎng)度的量詞如“*”、“+” 、“{m,n}”等是不支持的。
源字符串:
復(fù)制代碼 代碼如下:
import java.util.regex.*;
String test = "
但是如果源字符串變一下,加個(gè)屬性變成“
復(fù)制代碼 代碼如下:
String test = "cba";
String reg = "(?<=(c?b))a";
Matcher m = Pattern.compile(reg).matcher(test);
while(m.find())
{
System.out.println(m.group());
System.out.println(m.group(1));
}
/*--------輸出--------
a
*/
可以看到,“c?”并沒(méi)有參與匹配,在這里,“?”并不具備貪婪模式的作用,“?”只提供了一個(gè)分支的作用,共記錄了兩個(gè)分支,一個(gè)分支需要從當(dāng)前位置向前查找一個(gè)字符,另一個(gè)分支需要從當(dāng)前位置向前查找兩個(gè)字符。正則引擎從當(dāng)前位置,嘗試這兩種情況,優(yōu)先嘗試的是需要向前查找較少字符的分支,匹配成功,則不再嘗試另一個(gè)分支,只有這一分支匹配失敗時(shí),才會(huì)去嘗試另一個(gè)分支。
復(fù)制代碼 代碼如下:
String test = "dcba";
String reg = "(?<=(dc?b))a";
Matcher m = Pattern.compile(reg).matcher(test);
while(m.find())
{
System.out.println(m.group());
System.out.println(m.group(1));
}
/*--------輸出--------
a
dcb
*/
雖然有兩個(gè)分支,但向前查找的字符數(shù)可預(yù)知的,所以只支持“?”時(shí)并不復(fù)雜,但如果再支持其它不定長(zhǎng)度量詞,情況又如何呢?
2.1.3 .NET中逆序環(huán)視匹配原理
.NET的逆序環(huán)視中,是支持不定長(zhǎng)度量詞的,在這個(gè)時(shí)候,匹配過(guò)程就變得復(fù)雜了。先看一下定長(zhǎng)的是如何匹配的。
復(fù)制代碼 代碼如下:
string test = "
從結(jié)果可以看到,.NET中的逆序環(huán)視在子表達(dá)式長(zhǎng)度固定時(shí),匹配行為與Java中應(yīng)該是一樣的。那么不定長(zhǎng)量詞又如何呢?
復(fù)制代碼 代碼如下:
string test = "cba";
Regex reg = new Regex(@"(?<=(c?b))a");
Match m = reg.Match(test);
if (m.Success)
{
richTextBox2.Text += m.Value + "\n";
richTextBox2.Text += m.Groups[1].Value + "\n";
}
/*--------輸出--------
a
cb
*/
可以看到,這里的“?”具備了貪婪模式的特性。那么這個(gè)時(shí)候是否會(huì)有這樣的疑問(wèn),它的匹配過(guò)程仍然是從當(dāng)前位置向左嘗試,還是從字符串開(kāi)始位置向右嘗試匹配呢?
復(fù)制代碼 代碼如下:
string test = "
Match m = reg.Match(test);
if (m.Success)
{
richTextBox2.Text += m.Value + "\n";
richTextBox2.Text += m.Groups[1].Value + "\n";
}
/*--------輸出--------
a
從結(jié)果可看出,在逆序環(huán)視中有不定量詞的時(shí)候,仍然是從當(dāng)前位置,向左嘗試匹配的,否則Groups[1]的內(nèi)容就是“
復(fù)制代碼 代碼如下:
string test = "e
Match m = reg.Match(test);
if (m.Success)
{
richTextBox2.Text += m.Value + "\n";
richTextBox2.Text += m.Groups[1].Value + "\n";
}
/*--------輸出--------
a
可以看到,采用貪婪模式以后,雖然嘗試到“c”前面的“<”時(shí)已經(jīng)可以匹配成功,但由于是貪婪模式,還是要繼續(xù)嘗試匹配的。直到嘗試到開(kāi)始位置,取最長(zhǎng)的成功匹配作為匹配結(jié)果。
2.2 匹配過(guò)程
再來(lái)理一下逆序環(huán)視的匹配過(guò)程吧。
源字符串:
復(fù)制代碼 代碼如下:
string test = @" ** 這里是不固定的字符串1 **
** 這里是不固定的字符串2 **
** 這里是不固定的字符串3 ** ";
MatchCollection mc = Regex.Matches(test, @"(?<=)([\s\S]*?)(?=)");
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n---------------\n";
}
/*--------輸出--------
** 這里是不固定的字符串1 **
---------------
** 這里是不固定的字符串2 **
---------------
** 這里是不固定的字符串3 **
---------------
*/
其實(shí)真正讓人費(fèi)解的是這里的逆序環(huán)視的匹配結(jié)果,為了更好的說(shuō)明問(wèn)題,改下正則。
string test = @" ** 這里是不固定的字符串1 **
復(fù)制代碼 代碼如下:
** 這里是不固定的字符串2 **
** 這里是不固定的字符串3 ** ";
MatchCollection mc = Regex.Matches(test, @"(?<=())([\s\S]*?)(?=)");
for(int i=0;i
richTextBox2.Text += "第" + (i+1) + "輪成功匹配結(jié)果:\n";
richTextBox2.Text += "Group[0]:" + m.Value + "\n";
richTextBox2.Text += "Group[1]:" + m.Groups[1].Value + "\n---------------\n";
}
/*--------輸出--------
第1輪成功匹配結(jié)果:
Group[0]: ** 這里是不固定的字符串1 **
Group[1]:
---------------
第2輪成功匹配結(jié)果:
Group[0]:
** 這里是不固定的字符串2 **
Group[1]: ** 這里是不固定的字符串1 **
---------------
第3輪成功匹配結(jié)果:
Group[0]:
** 這里是不固定的字符串3 **
Group[1]: ** 這里是不固定的字符串2 **
---------------
*/
對(duì)于第一輪成功匹配結(jié)果應(yīng)該不存在什么疑問(wèn),這里不做解釋。
第一輪成功匹配結(jié)束的位置是第一個(gè)“”前的位置,第二輪成功匹配嘗試就是從這一位置開(kāi)始。
首先由“(?<=)”取得控制權(quán),向左查找6個(gè)字符后開(kāi)始嘗試匹配,由于“<”會(huì)匹配失敗,所以會(huì)一直嘗試到位置0處,這時(shí)“”要匹配成功,匹配的結(jié)束位置必須是第一個(gè)“”前的位置,所以“>”是匹配失敗的,這一位置整個(gè)表達(dá)式匹配失敗。
正則引擎?zhèn)鲃?dòng)裝置向右傳動(dòng),直到第一個(gè)“”后的位置,“”匹配成功,匹配開(kāi)始位置是位置0,匹配結(jié)束位置是第一個(gè)“”后的位置,“”匹配到的內(nèi)容是“ ** 這里是不固定的字符串1 ** ”,其中“[\s\S]*?”匹配到的內(nèi)容是“color="#008000"> ** 這里是不固定的字符串1 ** 接下來(lái)的第三輪成功匹配,匹配過(guò)程與第二輪基本相同,只不過(guò)由于使用的是非貪婪模式,所以“”在匹配到“ ** 這里是不固定的字符串2 ** ”時(shí)匹配成功,就結(jié)束匹配,不再向左嘗試匹配了。
接下來(lái)看下貪婪模式的匹配結(jié)果。
復(fù)制代碼 代碼如下:
string test = @" ** 這里是不固定的字符串1 **
** 這里是不固定的字符串2 **
** 這里是不固定的字符串3 ** ";
MatchCollection mc = Regex.Matches(test, @"(?<=())([\s\S]*?)(?=)");
for(int i=0;i
richTextBox2.Text += "第" + (i+1) + "輪成功匹配結(jié)果:\n";
richTextBox2.Text += "Group[0]:" + m.Value + "\n";
richTextBox2.Text += "Group[1]:" + m.Groups[1].Value + "\n---------------\n";
}
/*--------輸出--------
第1輪匹配結(jié)果:
Group[0]: ** 這里是不固定的字符串1 **
Group[1]:
---------------
第2輪匹配結(jié)果:
Group[0]:
** 這里是不固定的字符串2 **
Group[1]: ** 這里是不固定的字符串1 **
---------------
第3輪匹配結(jié)果:
Group[0]:
** 這里是不固定的字符串3 **
Group[1]: ** 這里是不固定的字符串1 **
** 這里是不固定的字符串2 **
---------------
*/
僅僅是一個(gè)字符的差別,整個(gè)表達(dá)式的匹配結(jié)果沒(méi)有變化,但匹配過(guò)程差別卻是很大的。
那么如果想得到下面這種結(jié)果要如何做呢?
/*--------輸出--------
** 這里是不固定的字符串1 **
---------------
** 這里是不固定的字符串2 **
---------------
** 這里是不固定的字符串3 **
---------------
*/
把量詞修飾的子表達(dá)式的匹配范圍縮小就可以了。
復(fù)制代碼 代碼如下:
string test = @" ** 這里是不固定的字符串1 **
** 這里是不固定的字符串2 **
** 這里是不固定的字符串3 ** ";
MatchCollection mc = Regex.Matches(test, @"(?is)(?<=(]*>))(?:(?!?font\b).)*(?=)");
for(int i=0;i
richTextBox2.Text += "第" + (i+1) + "輪匹配結(jié)果:\n";
richTextBox2.Text += "Group[0]:" + mc[i].Value + "\n";
richTextBox2.Text += "Group[1]:" + mc[i].Groups[1].Value + "\n---------------\n";
}
/*--------輸出--------
第1輪匹配結(jié)果:
Group[0]: ** 這里是不固定的字符串1 **
Group[1]:
---------------
第2輪匹配結(jié)果:
Group[0]: ** 這里是不固定的字符串2 **
Group[1]:
---------------
第3輪匹配結(jié)果:
Group[0]: ** 這里是不固定的字符串3 **
Group[1]:
---------------
*/
3.2 逆序環(huán)視應(yīng)用總結(jié)
通過(guò)對(duì)逆序環(huán)視的分析,可以看出,逆序環(huán)視中使用不定長(zhǎng)度的量詞,匹配過(guò)程很復(fù)雜,代價(jià)也是很大的,這也許也是目前絕大多數(shù)語(yǔ)言不支持逆序環(huán)視,或是不支持在逆序環(huán)視中使用不定長(zhǎng)度量詞的原因吧。
在正則應(yīng)用中需要注意的幾點(diǎn):
1、 不要輕易在逆序環(huán)視中使用不定長(zhǎng)度的量詞,除非確實(shí)需要;
2、 在任何場(chǎng)景下,不只是逆序環(huán)視中,不要輕易使用量詞修飾匹配范圍非常大的子表達(dá)式,小數(shù)點(diǎn)“.”和“[\s\S]”之類的,使用時(shí)尤其要注意。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“正則表達(dá)式如何實(shí)現(xiàn)逆序環(huán)視”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!