久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

正則基礎之環視的示例分析

126次閱讀
沒有評論

共計 4598 個字符,預計需要花費 12 分鐘才能閱讀完成。

這篇文章給大家分享的是有關正則基礎之環視的示例分析的內容。丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,一起跟隨丸趣 TV 小編過來看看吧。

環視(Lookaround)

1  環視基礎

環視只進行子表達式的匹配,不占有字符,匹配到的內容不保存到最終的匹配結果,是零寬度的。環視匹配的最終結果就是一個位置。

環視的作用相當于對所在位置加了一個附加條件,只有滿足這個條件,環視子表達式才能匹配成功。

環視按照方向劃分有順序和逆序兩種,按照是否匹配有肯定和否定兩種,組合起來就有四種環視。順序環視相當于在當前位置右側附加一個條件,而逆序環視相當于在當前位置左側附加一個條件。

表達式

說明

(? =Expression)

逆序肯定環視,表示所在位置左側能夠匹配 Expression

(? !Expression)

逆序否定環視,表示所在位置左側不能匹配 Expression

(?=Expression)

順序肯定環視,表示所在位置右側能夠匹配 Expression

(?!Expression)

順序否定環視,表示所在位置右側不能匹配 Expression

  對于環視的叫法,有的文檔里叫預搜索,有的叫什么什么斷言的,這里使用了更多人容易接受的《精通正則表達式》中“環視”的叫法,其實叫什么無所謂,只要知道是什么作用就是了,就這么幾個語法規則,  還是很容易記的

2  環視匹配原理

  環視是正則中的一個難點,對于環視的理解,可以從應用和原理兩個角度理解,如果想理解得更清晰、深入一些,還是從原理的角度理解好一些,正則匹配基本原理參考 NFA 引擎匹配原理。

上面提到環視相當于對“所在位置”附加了一個條件,環視的難點在于找到這個“位置”,這一點解決了,環視也就沒什么秘密可言了。

順序環視匹配過程

對于順序肯定環視 (?=Expression) 來說,當子表達式 Expression 匹配成功時,(?=Expression)匹配成功,并報告 (?=Expression) 匹配當前位置成功。

對于順序否定環視 (?!Expression) 來說,當子表達式 Expression 匹配成功時,(?!Expression)匹配失敗;當子表達式 Expression 匹配失敗時,(?!Expression)匹配成功,并報告 (?!Expression) 匹配當前位置成功;

順序肯定環視的例子已在 NFA 引擎匹配原理中講解過了,這里再講解一下順序否定環視。

源字符串:aa p one /p bb p two /p cc

正則表達式:(?!/?pb)[^]+

這個正則的意義就是匹配除 p… 或 /p 之外的其余標簽。

匹配過程:

首先由字符“”取得控制權,從位置 0 開始匹配,由于“”匹配“a”失敗,在位置 0 處整個表達式匹配失敗,第一次迭代匹配失敗,正則引擎向前傳動,由位置 1 處開始嘗試第二次迭代匹配。

重復以上過程,直到位置 2,“”匹配“”成功,控制權交給“(?!/?pb)”;“(?!/?pb)”子表達式取得控制權后,進行內部子表達式的匹配。首先由“/?”取得控制權,嘗試匹配“p”失敗,進行回溯,不匹配,控制權交給“p”;由“p”來嘗試匹配“p”,匹配成功,控制權交給“b”;由“b”來嘗試匹配位置 4,匹配成功。此時子表達式匹配完成,“/?pb”匹配成功,那么環視表達式“(?!/?pb)”就匹配失敗。在位置 2 處整個表達式匹配失敗,新一輪迭代匹配失敗,正則引擎向前傳動,由位置 3 處開始嘗試下一輪迭代匹配。

在位置 8 處也會遇到一輪“/?pb”匹配“/p”成功,而導致環視表達式“(?!/?pb)”匹配失敗,從而導致整個表達式匹配失敗的過程。

重復以上過程,直到位置 14,“”匹配“”成功,控制權交給“(?!/?pb)”;“/?”嘗試匹配“d”失敗,進行回溯,不匹配,控制權交給“p”;由“p”來嘗試匹配“d”,匹配失敗,已經沒有備選狀態可供回溯,匹配失敗。此時子表達式匹配完成,“/?pb”匹配失敗,那么環視表達式“(?!/?pb)”就匹配成功。匹配的結果是位置 15,然后控制權交給“[^]+”;由“[^]+”從位置 15 進行嘗試匹配,可以成功匹配到“p”,控制權交給“”;由“”來匹配“”。

此時正則表達式匹配完成,報告匹配成功。匹配結果為“p”,開始位置為 14,結束位置為 19。其中“”匹配“”,“(?!/?pb)”匹配位置 15,“[^]+”匹配字符串“p”,“”匹配“”。

逆序環視基礎

對于逆序肯定環視 (? =Expression) 來說,當子表達式 Expression 匹配成功時,(? =Expression)匹配成功,并報告 (? =Expression) 匹配當前位置成功。

對于逆序否定環視 (? !Expression) 來說,當子表達式 Expression 匹配成功時,(? !Expression)匹配失敗;當子表達式 Expression 匹配失敗時,(? !Expression)匹配成功,并報告 (? !Expression) 匹配當前位置成功;

順序環視相當于在當前位置右側附加一個條件,所以它的匹配嘗試是從當前位置開始的,然后向右嘗試匹配,直到某一位置使得匹配成功或失敗為止。而逆序環視的特殊處在于,它相當于在當前位置左側附加一個條件,所以它不是在當前位置開始嘗試匹配的,而是從當前位置左側某一位置開始,匹配到當前位置為止,報告匹配成功或失敗。

順序環視嘗試匹配的起點是確定的,就是當前位置,而匹配的終點是不確定的。逆序環視匹配的起點是不確定的,是當前位置左側某一位置,而匹配的終點是確定的,就是當前位置。

所以順序環視相對是簡單的,而逆序環視相對是復雜的。這也就是為什么大多數語言和工具都提供了對順序環視的支持,而只有少數語言提供了對逆序環視支持的原因。

JavaScript 中只支持順序環視,不支持逆序環視。

Java 中雖然順序環視和逆序環視都支持,但是逆序環視只支持長度確定的表達式,逆序環視中量詞只支持“?”,不支持其它長度不定的量詞。長度確定時,引擎可以向左查找固定長度的位置作為起點開始嘗試匹配,而如果長度不確定時,就要從當前位置向左逐個位置開始嘗試匹配,不成功則回溯,再向左側位置進行嘗試匹配,然后重復以上過程,直到匹配成功,或是嘗試到位置 0 處以后,報告匹配失敗,處理的復雜度是顯而易見的。

目前只有.NET 中支持不確定長度的逆序環視。

逆序環視匹配過程

源字符串:p a test /p

正則表達式:(? = p)[^]+(?= /p)

這個正則的意義就是匹配 p 和 /p 標簽之間的內容,而不包括 p 和 /p 標簽本身。

匹配過程:

首先由“(? = p)”取得控制權,從位置 0 開始匹配,由于位置 0 是起始位置,左側沒有任何內容,所以“p”必然匹配失敗,從而環視表達式“(? = p)”匹配失敗,導致整個表達式在位置 0 處匹配失敗。第一輪迭代匹配失敗,正則引擎向前傳動,由位置 1 處開始嘗試第二次迭代匹配。

直到傳動到位置 5,“(? = p)”取得控制權,向左查找 5 個位置,由位置 0 開始匹配,由“p”匹配“p”成功,從而“(? = p)”匹配成功,匹配的結果為位置 5,控制權交給“[^]+”;“[^]+”從位置 5 開始嘗試匹配,匹配“a test”成功,控制權交給“(?= /p)”;由“/p”匹配“/p”成功,從而“(?= /p)”匹配成功,匹配結果為位置 11。

此時正則表達式匹配完成,報告匹配成功。匹配結果為“a test”,開始位置為 5,結束位置為 11。其中“(? = p)”匹配位置 5,“[^]+”匹配“a test”,“(?= /p)”匹配位置 11。

逆序否定環視的匹配過程與上述過程類似,區別只是當 Expression 匹配失敗時,逆序否定表達式 (? !Expression) 才匹配成功。

到此環視的匹配原理已基本講解完,環視也就沒有什么秘密可言了,所需要的,也只是多加練習而已。

3  環視應用

今天寫累了,暫時就給出一個環視的綜合應用實例吧,至于環視的應用場景和技巧,后面再整理。

需求:數字格式化成用“,”的貨幣格式。

正則表達式:(?n)(? =d)(? !.d*)(?=(d{3})+(.|$))

測試代碼:

double[] data = new double[] {0, 12, 123, 1234, 12345, 123456, 1234567, 123456789, 1234567890, 12.345, 123.456, 1234.56, 12345.6789, 123456.789, 1234567.89, 12345678.9};

foreach (double d in data)

{

  richTextBox2.Text += 源字符串:+ d.ToString().PadRight(15) + 格式化:+ Regex.Replace(d.ToString(), @ (?n)(? =d)(? !.d*)(?=(d{3})+(.|$)) , , ) + n

}

輸出結果:

源字符串:0  格式化:0

源字符串:12  格式化:12

源字符串:123  格式化:123

源字符串:1234  格式化:1,234

源字符串:12345  格式化:12,345

源字符串:123456  格式化:123,456

源字符串:1234567  格式化:1,234,567

源字符串:123456789  格式化:123,456,789

源字符串:1234567890  格式化:1,234,567,890

源字符串:12.345  格式化:12.345

源字符串:123.456  格式化:123.456

源字符串:1234.56  格式化:1,234.56

源字符串:12345.6789  格式化:12,345.6789

源字符串:123456.789  格式化:123,456.789

源字符串:1234567.89  格式化:1,234,567.89

源字符串:12345678.9  格式化:12,345,678.9

實現分析:

首先根據需求可以確定是把一些特定的位置替換為“,”,接下來就是分析并找到這些位置的規律,并抽象出來以正則表達式來表示。

1、  這個位置的左側必須為數字

2、  這個位置右側到出現“.”或結尾為止,必須是數字,且數字的個數必須為 3 的倍數

3、  這個位置左側相隔任意個數字不能出現“.”

由以上三條,就可以完全確定這些位置,只要實現以上三條,組合一下正則表達式就可以了。

根據分析,最終匹配的結果是一個位置,所以所有子表達式都要求是零寬度。

1、  是對當前所在位置左側附加的條件,所以要用到逆序環視,因為要求必須出現,所以是肯定的,符合這一條件的子表達式即為“(? =d)”

2、  是對當前所在位置右側附加的條件,所以要用到順序環視,也是要求出現,所以是肯定的,是數字,且個數為 3 的倍數,即“(?=(d{3})+)”,到出現“.”或結尾為止,即“(?=(d{3})+(.|$))”

3、  是對當前所在位置左側附加的條件,所以要用到逆序環視,因為要求不能出現,所以是否定的,即“(? !.d*)”

因為零寬度的子表達式是非互斥的,最后匹配的都是同一個位置,所以先后順序是不影響最后的匹配結果的,可以任意組合,只是習慣上把逆序環視寫在左側,順序環視寫在右側。

說明:這里只是為了說明環視的使用而舉的一個例子,實際上這個需求直接用 string.Format 就可以做到

感謝各位的閱讀!關于“正則基礎之環視的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-15發表,共計4598字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 永济市| 额敏县| 蒙山县| 卢龙县| 日照市| 南汇区| 扬中市| 焉耆| 盱眙县| 井研县| 池州市| 昆山市| 柞水县| 盐亭县| 涞水县| 始兴县| 托里县| 涞源县| 兰考县| 惠州市| 赫章县| 科技| 望都县| 军事| 太谷县| 司法| 靖州| 锡林浩特市| 龙川县| 龙口市| 通许县| 东港市| 旅游| 乡城县| 江永县| 南汇区| 托克逊县| 措美县| 开封市| 墨脱县| 仙游县|