å‰è¨€ï¼š
       本文是å‰ä¸€ç‰‡æ–‡ç« 《深入浅出之æ£åˆ™è¡¨è¾¾å¼ï¼ˆä¸€ï¼‰ã€‹çš„ç»ç¯‡ï¼Œåœ¨æœ¬æ–‡ä¸è®²è¿°äº†æ£åˆ™è¡¨è¾¾å¼ä¸çš„组与å‘åŽå¼•用,先å‰å‘åŽæŸ¥çœ‹ï¼Œæ¡ä»¶æµ‹è¯•,å•è¯è¾¹ç•Œï¼Œé€‰æ‹©ç¬¦ç‰è¡¨è¾¾å¼åŠä¾‹å,并分æžäº†æ£åˆ™å¼•æ“Žåœ¨æ‰§è¡ŒåŒ¹é…æ—¶çš„内部机ç†ã€‚
      本文是Jan Goyvaerts为RegexBuddy写的教程的译文,版æƒå½’原作者所有,欢迎转载。但是为了尊é‡åŽŸä½œè€…å’Œè¯‘è€…çš„åŠ³åŠ¨ï¼Œè¯·æ³¨æ˜Žå‡ºå¤„ï¼è°¢è°¢ï¼
Â
9.     å•è¯è¾¹ç•Œ
å…ƒå—符<<\b>>也是一ç§å¯¹ä½ç½®è¿›è¡ŒåŒ¹é…的“锚â€ã€‚è¿™ç§åŒ¹é…是0长度匹é…。
有4ç§ä½ç½®è¢«è®¤ä¸ºæ˜¯â€œå•è¯è¾¹ç•Œâ€ï¼š
1)       在å—符串的第一个å—符å‰çš„ä½ç½®(如果å—符串的第一个å—符是一个“å•è¯å—符â€)
2)       在å—符串的最åŽä¸€ä¸ªå—符åŽçš„ä½ç½®(如果å—符串的最åŽä¸€ä¸ªå—符是一个“å•è¯å—符â€)
3)       在一个“å•è¯å—符â€å’Œâ€œéžå•è¯å—符â€ä¹‹é—´ï¼Œå…¶ä¸â€œéžå•è¯å—符â€ç´§è·Ÿåœ¨â€œå•è¯å—符â€ä¹‹åŽ
4)       在一个“éžå•è¯å—符â€å’Œâ€œå•è¯å—符â€ä¹‹é—´ï¼Œå…¶ä¸â€œå•è¯å—符â€ç´§è·Ÿåœ¨â€œéžå•è¯å—符â€åŽé¢
 “å•è¯å—ç¬¦â€æ˜¯å¯ä»¥ç”¨â€œ\wâ€åŒ¹é…çš„å—符,“éžå•è¯å—ç¬¦â€æ˜¯å¯ä»¥ç”¨â€œ\Wâ€åŒ¹é…çš„å—符。在大多数的æ£åˆ™è¡¨è¾¾å¼å®žçްä¸ï¼Œâ€œå•è¯å—符â€é€šå¸¸åŒ…括<<[a-zA-Z0-9_]>>。
例如:<<\b4\b>>能够匹é…å•个的4è€Œä¸æ˜¯ä¸€ä¸ªæ›´å¤§æ•°çš„一部分。这个æ£åˆ™è¡¨è¾¾å¼ä¸ä¼šåŒ¹é…“44â€ä¸çš„4。
æ¢ç§è¯´æ³•ï¼Œå‡ ä¹Žå¯ä»¥è¯´<<\b>>匹é…ä¸€ä¸ªâ€œå—æ¯æ•°å—åºåˆ—â€çš„开始和结æŸçš„ä½ç½®ã€‚
Â
“å•è¯è¾¹ç•Œâ€çš„å–å集为<<\B>>,他è¦åŒ¹é…çš„ä½ç½®æ˜¯ä¸¤ä¸ªâ€œå•è¯å—符â€ä¹‹é—´æˆ–者两个“éžå•è¯å—符â€ä¹‹é—´çš„ä½ç½®ã€‚
Â
·       深入æ£åˆ™è¡¨è¾¾å¼å¼•擎内部
让我们看看把æ£åˆ™è¡¨è¾¾å¼<<\bis\b>>应用到å—符串“This island is beautifulâ€ã€‚引擎先处ç†ç¬¦å·<<\b>>ã€‚å› ä¸º\b是0长度 ,所以第一个å—符Tå‰é¢çš„ä½ç½®ä¼šè¢«è€ƒå¯Ÿã€‚å› ä¸ºT是一个“å•è¯å—符â€ï¼Œè€Œå®ƒå‰é¢çš„å—符是一个空å—符(void),所以\b匹é…了å•è¯è¾¹ç•Œã€‚接ç€<<i>>和第一个å—符“Tâ€åŒ¹é…失败。匹é…过程继ç»è¿›è¡Œï¼Œç›´åˆ°ç¬¬äº”ä¸ªç©ºæ ¼ç¬¦ï¼Œå’Œç¬¬å››ä¸ªå—符“sâ€ä¹‹é—´åˆåŒ¹é…了<<\b>>ã€‚ç„¶è€Œç©ºæ ¼ç¬¦å’Œ<<i>>ä¸åŒ¹é…。继ç»å‘åŽï¼Œåˆ°äº†ç¬¬å…个å—符“iâ€ï¼Œå’Œç¬¬äº”ä¸ªç©ºæ ¼å—符之间匹é…了<<\b>>,然åŽ<<is>>和第å…ã€ç¬¬ä¸ƒä¸ªå—符都匹é…了。然而第八个å—符和第二个“å•è¯è¾¹ç•Œâ€ä¸åŒ¹é…,所以匹é…åˆå¤±è´¥äº†ã€‚到了第13个å—符iï¼Œå› ä¸ºå’Œå‰é¢ä¸€ä¸ªç©ºæ ¼ç¬¦å½¢æˆâ€œå•è¯è¾¹ç•Œâ€ï¼ŒåŒæ—¶<<is>>和“isâ€åŒ¹é…。引擎接ç€å°è¯•匹é…第二个<<\b>>ã€‚å› ä¸ºç¬¬15ä¸ªç©ºæ ¼ç¬¦å’Œâ€œsâ€å½¢æˆå•è¯è¾¹ç•Œï¼Œæ‰€ä»¥åŒ¹é…æˆåŠŸã€‚å¼•æ“Žâ€œæ€¥ç€â€è¿”回æˆåŠŸåŒ¹é…的结果。
Â
10. 选择符
æ£åˆ™è¡¨è¾¾å¼ä¸â€œ|â€è¡¨ç¤ºé€‰æ‹©ã€‚ä½ å¯ä»¥ç”¨é€‰æ‹©ç¬¦åŒ¹é…多个å¯èƒ½çš„æ£åˆ™è¡¨è¾¾å¼ä¸çš„一个。
å¦‚æžœä½ æƒ³æœç´¢æ–‡å—“catâ€æˆ–“dogâ€ï¼Œä½ å¯ä»¥ç”¨<<cat|dog>>ã€‚å¦‚æžœä½ æƒ³æœ‰æ›´å¤šçš„é€‰æ‹©ï¼Œä½ åªè¦æ‰©å±•列表<<cat|dog|mouse|fish>>。
选择符在æ£åˆ™è¡¨è¾¾å¼ä¸å…·æœ‰æœ€ä½Žçš„优先级,也就是说,它告诉引擎è¦ä¹ˆåŒ¹é…选择符左边的所有表达å¼ï¼Œè¦ä¹ˆåŒ¹é…å³è¾¹çš„æ‰€æœ‰è¡¨è¾¾å¼ã€‚ä½ ä¹Ÿå¯ä»¥ç”¨åœ†æ‹¬å·æ¥é™åˆ¶é€‰æ‹©ç¬¦çš„作用范围。如<<\b(cat|dog)\b>>ï¼Œè¿™æ ·å‘Šè¯‰æ£åˆ™å¼•擎把(cat|dog)当æˆä¸€ä¸ªæ£åˆ™è¡¨è¾¾å¼å•使¥å¤„ç†ã€‚
Â·Â Â Â Â Â Â Â æ³¨æ„æ£åˆ™å¼•æ“Žçš„â€œæ€¥äºŽè¡¨åŠŸâ€æ€§
æ£åˆ™å¼•æ“Žæ˜¯æ€¥åˆ‡çš„ï¼Œå½“å®ƒæ‰¾åˆ°ä¸€ä¸ªæœ‰æ•ˆçš„åŒ¹é…æ—¶ï¼Œå®ƒä¼šåœæ¢æœç´¢ã€‚å› æ¤åœ¨ä¸€å®šæ¡ä»¶ä¸‹ï¼Œé€‰æ‹©ç¬¦ä¸¤è¾¹çš„表达å¼çš„顺åºå¯¹ç»“果会有影å“。å‡è®¾ä½ 想用æ£åˆ™è¡¨è¾¾å¼æœç´¢ä¸€ä¸ªç¼–程è¯è¨€çš„函数列表:Get,GetValue,Set或SetValue。一个明显的解决方案是<<Get|GetValue|Set|SetValue>>。让我们看看当æœç´¢SetValue时的结果。
å› ä¸º<<Get>>å’Œ<<GetValue>>都失败了,而<<Set>>åŒ¹é…æˆåŠŸã€‚å› ä¸ºæ£åˆ™å¯¼å‘的引擎都是“急切â€çš„,所以它会返回第一个æˆåŠŸçš„åŒ¹é…,就是“Setâ€ï¼Œè€Œä¸åŽ»ç»§ç»æœç´¢æ˜¯å¦æœ‰å…¶ä»–更好的匹é…。
和我们期望的相å,æ£åˆ™è¡¨è¾¾å¼å¹¶æ²¡æœ‰åŒ¹é…整个å—ç¬¦ä¸²ã€‚æœ‰å‡ ç§å¯èƒ½çš„解决办法。一是考虑到æ£åˆ™å¼•æ“Žçš„â€œæ€¥åˆ‡â€æ€§ï¼Œæ”¹å˜é€‰é¡¹çš„顺åºï¼Œä¾‹å¦‚我们使用<<GetValue|Get|SetValue|Set>>ï¼Œè¿™æ ·æˆ‘ä»¬å°±å¯ä»¥ä¼˜å…ˆæœç´¢æœ€é•¿çš„匹é…。我们也å¯ä»¥æŠŠå››ä¸ªé€‰é¡¹ç»“åˆèµ·æ¥æˆä¸¤ä¸ªé€‰é¡¹ï¼š<<Get(Value)?|Set(Value)?>>ã€‚å› ä¸ºé—®å·é‡å¤ç¬¦æ˜¯è´ªå©ªçš„,所以SetValue总会在Set之å‰è¢«åŒ¹é…。
一个更好的方案是使用å•è¯è¾¹ç•Œï¼š<<\b(Get|GetValue|Set|SetValue)\b>>或<<\b(Get(Value)?|Set(Value)?\b>>。更进一æ¥ï¼Œæ—¢ç„¶æ‰€æœ‰çš„选择都有相åŒçš„结尾,我们å¯ä»¥æŠŠæ£åˆ™è¡¨è¾¾å¼ä¼˜åŒ–为<<\b(Get|Set)(Value)?\b>>。
Â
Â
11. 组与å‘åŽå¼•用
把æ£åˆ™è¡¨è¾¾å¼çš„一部分放在圆括å·å†…ï¼Œä½ å¯ä»¥å°†å®ƒä»¬å½¢æˆç»„。然åŽä½ å¯ä»¥å¯¹æ•´ä¸ªç»„使用一些æ£åˆ™æ“作,例如é‡å¤æ“作符。
è¦æ³¨æ„çš„æ˜¯ï¼Œåªæœ‰åœ†æ‹¬å·â€œ()â€æ‰èƒ½ç”¨äºŽå½¢æˆç»„。“[]â€ç”¨äºŽå®šä¹‰å—符集。“{}â€ç”¨äºŽå®šä¹‰é‡å¤æ“作。
当用“()â€å®šä¹‰äº†ä¸€ä¸ªæ£åˆ™è¡¨è¾¾å¼ç»„åŽï¼Œæ£åˆ™å¼•擎则会把被匹é…的组按照顺åºç¼–å·ï¼Œå˜å…¥ç¼“å˜ã€‚当对被匹é…的组进行å‘åŽå¼•用的时候,å¯ä»¥ç”¨â€œ\æ•°å—â€çš„æ–¹å¼è¿›è¡Œå¼•用。<<\1>>引用第一个匹é…çš„åŽå‘引用组,<<\2>>引用第二个组,以æ¤ç±»æŽ¨ï¼Œ<<\n>>引用第n个组。而<<\0>>则引用整个被匹é…çš„æ£åˆ™è¡¨è¾¾å¼æœ¬èº«ã€‚我们看一个例å。
å‡è®¾ä½ 想匹é…一个HTMLæ ‡ç¾çš„å¼€å§‹æ ‡ç¾å’Œç»“æŸæ ‡ç¾ï¼Œä»¥åŠæ ‡ç¾ä¸é—´çš„æ–‡æœ¬ã€‚比如<B>This is a test</B>,我们è¦åŒ¹é…<B>å’Œ</B>以åŠä¸é—´çš„æ–‡å—。我们å¯ä»¥ç”¨å¦‚下æ£åˆ™è¡¨è¾¾å¼ï¼šâ€œ<([A-Z][A-Z0-9]*)[^>]*>.*?</\1>â€
首先,“<â€å°†ä¼šåŒ¹é…“<B>â€çš„第一个å—符“<â€ã€‚ç„¶åŽ[A-Z]匹é…B,[A-Z0-9]*将会匹é…0åˆ°å¤šæ¬¡å—æ¯æ•°å—,åŽé¢ç´§æŽ¥ç€0到多个éžâ€œ>â€çš„å—ç¬¦ã€‚æœ€åŽæ£åˆ™è¡¨è¾¾å¼çš„“>â€å°†ä¼šåŒ¹é…“<B>â€çš„“>â€ã€‚æŽ¥ä¸‹æ¥æ£åˆ™å¼•æ“Žå°†å¯¹ç»“æŸæ ‡ç¾ä¹‹å‰çš„å—符进行惰性匹é…,直到é‡åˆ°ä¸€ä¸ªâ€œ</â€ç¬¦å·ã€‚ç„¶åŽæ£åˆ™è¡¨è¾¾å¼ä¸çš„“\1â€è¡¨ç¤ºå¯¹å‰é¢åŒ¹é…的组“([A-Z][A-Z0-9]*)â€è¿›è¡Œå¼•用,在本例ä¸ï¼Œè¢«å¼•ç”¨çš„æ˜¯æ ‡ç¾å“Bâ€ã€‚所以需è¦è¢«åŒ¹é…çš„ç»“å°¾æ ‡ç¾ä¸ºâ€œ</B>â€
ä½ å¯ä»¥å¯¹ç›¸åŒçš„åŽå‘引用组进行多次引用,<<([a-c])x\1x\1>>将匹é…“axaxaâ€ã€â€œbxbxbâ€ä»¥åŠâ€œcxcxcâ€ã€‚如果用数å—å½¢å¼å¼•用的组没有有效的匹é…,则引用到的内容简å•的为空。
一个åŽå‘引用ä¸èƒ½ç”¨äºŽå®ƒè‡ªèº«ã€‚<<([abc]\1)>>æ˜¯é”™è¯¯çš„ã€‚å› æ¤ä½ ä¸èƒ½å°†<<\0>>用于一个æ£åˆ™è¡¨è¾¾å¼åŒ¹é…本身,它åªèƒ½ç”¨äºŽæ›¿æ¢æ“作ä¸ã€‚
åŽå‘引用ä¸èƒ½ç”¨äºŽå—符集内部。<<(a)[\1b]>>ä¸çš„<<\1>>å¹¶ä¸è¡¨ç¤ºåŽå‘引用。在å—符集内部,<<\1>>å¯ä»¥è¢«è§£é‡Šä¸ºå…«è¿›åˆ¶å½¢å¼çš„转ç 。
å‘åŽå¼•用会é™ä½Žå¼•æ“Žçš„é€Ÿåº¦ï¼Œå› ä¸ºå®ƒéœ€è¦å˜å‚¨åŒ¹é…çš„ç»„ã€‚å¦‚æžœä½ ä¸éœ€è¦å‘åŽå¼•ç”¨ï¼Œä½ å¯ä»¥å‘Šè¯‰å¼•擎对æŸä¸ªç»„ä¸å˜å‚¨ã€‚例如:<<Get(?:Value)>>。其ä¸â€œ(â€åŽé¢ç´§è·Ÿçš„“?:â€ä¼šå‘Šè¯‰å¼•擎对于组(Value),ä¸å˜å‚¨åŒ¹é…的值以供åŽå‘引用。
·       é‡å¤æ“作与åŽå‘引用
当对组使用é‡å¤æ“作符时,缓å˜é‡ŒåŽå‘å¼•ç”¨å†…å®¹ä¼šè¢«ä¸æ–刷新,åªä¿ç•™æœ€åŽåŒ¹é…的内容。例如:<<([abc]+)=\1>>将匹é…“cab=cabâ€ï¼Œä½†æ˜¯<<([abc])+=\1>>å´ä¸ä¼šã€‚å› ä¸º([abc])第一次匹é…“câ€æ—¶ï¼Œâ€œ\1â€ä»£è¡¨â€œcâ€ï¼›ç„¶åŽ([abc])会继ç»åŒ¹é…“aâ€å’Œâ€œbâ€ã€‚最åŽâ€œ\1â€ä»£è¡¨â€œbâ€ï¼Œæ‰€ä»¥å®ƒä¼šåŒ¹é…“cab=bâ€ã€‚
应用:检查é‡å¤å•è¯–å½“ç¼–è¾‘æ–‡å—æ—¶ï¼Œå¾ˆå®¹æ˜“就会输入é‡å¤å•è¯ï¼Œä¾‹å¦‚“the theâ€ã€‚使用<<\b(\w+)\s+\1\b>>å¯ä»¥æ£€æµ‹åˆ°è¿™äº›é‡å¤å•è¯ã€‚è¦åˆ 除第二个å•è¯ï¼Œåªè¦ç®€å•的利用替æ¢åŠŸèƒ½æ›¿æ¢æŽ‰â€œ\1â€å°±å¯ä»¥äº†ã€‚
Â
Â
·       组的命å和引用
在PHP,Pythonä¸ï¼Œå¯ä»¥ç”¨<<(?P<name>group)>>æ¥å¯¹ç»„进行命å。在本例ä¸ï¼Œè¯æ³•?P<name>就是对组(group)进行了命å。其ä¸nameæ˜¯ä½ å¯¹ç»„çš„èµ·çš„åå—ã€‚ä½ å¯ä»¥ç”¨(?P=name)进行引用。
.NET的命å组
.NET framework也支æŒå‘½å组。ä¸å¹¸çš„æ˜¯ï¼Œå¾®è½¯çš„程åºå‘˜ä»¬å†³å®šå‘æ˜Žä»–ä»¬è‡ªå·±çš„è¯æ³•ï¼Œè€Œä¸æ˜¯æ²¿ç”¨Perlã€Python的规则。目å‰ä¸ºæ¢ï¼Œè¿˜æ²¡æœ‰ä»»ä½•å…¶ä»–çš„æ£åˆ™è¡¨è¾¾å¼å®žçŽ°æ”¯æŒå¾®è½¯å‘æ˜Žçš„è¯æ³•。
䏋颿˜¯.NETä¸çš„例å:
(?<first>group)(?’second’group)
æ£å¦‚ä½ æ‰€çœ‹åˆ°çš„ï¼Œ.NETæä¾›ä¸¤ç§è¯æ³•æ¥åˆ›å»ºå‘½å组:一是用尖括å·â€œ<>â€ï¼Œæˆ–者用å•引å·â€œâ€™â€™â€ã€‚尖括å·åœ¨å—符串ä¸ä½¿ç”¨æ›´æ–¹ä¾¿ï¼Œå•引å·åœ¨ASP代ç 䏿›´æœ‰ç”¨ï¼Œå› 为ASP代ç ä¸â€œ<>â€è¢«ç”¨ä½œHTMLæ ‡ç¾ã€‚
è¦å¼•用一个命å组,使用\k<name>或\k’name’.
当进行æœç´¢æ›¿æ¢æ—¶ï¼Œä½ å¯ä»¥ç”¨â€œ${name}â€æ¥å¼•用一个命å组。
Â
12. æ£åˆ™è¡¨è¾¾å¼çš„åŒ¹é…æ¨¡å¼
本教程所讨论的æ£åˆ™è¡¨è¾¾å¼å¼•擎都支æŒä¸‰ç§åŒ¹é…模å¼ï¼š
<</i>>使æ£åˆ™è¡¨è¾¾å¼å¯¹å¤§å°å†™ä¸æ•感,
<</s>>å¼€å¯â€œå•行模å¼â€ï¼Œå³ç‚¹å·â€œ.â€åŒ¹é…新行符
<</m>>å¼€å¯â€œå¤šè¡Œæ¨¡å¼â€ï¼Œå³â€œ^â€å’Œâ€œ$â€åŒ¹é…新行符的å‰é¢å’ŒåŽé¢çš„ä½ç½®ã€‚
Â
·       在æ£åˆ™è¡¨è¾¾å¼å†…éƒ¨æ‰“å¼€æˆ–å…³é—æ¨¡å¼
å¦‚æžœä½ åœ¨æ£åˆ™è¡¨è¾¾å¼å†…部æ’入修饰符(?ism),则该修饰符åªå¯¹å…¶å³è¾¹çš„æ£åˆ™è¡¨è¾¾å¼èµ·ä½œç”¨ã€‚(?-i)是关é—大å°å†™ä¸æ•æ„Ÿã€‚ä½ å¯ä»¥å¾ˆå¿«çš„进行测试。<<(?i)te(?-i)st>>应该匹é…TEst,但是ä¸èƒ½åŒ¹é…teST或TEST.
Â
13. 原å组与防æ¢å›žæº¯
åœ¨ä¸€äº›ç‰¹æ®Šæƒ…å†µä¸‹ï¼Œå› ä¸ºå›žæº¯ä¼šä½¿å¾—å¼•æ“Žçš„æ•ˆçŽ‡æžå…¶ä½Žä¸‹ã€‚
让我们看一个例å:è¦åŒ¹é…è¿™æ ·çš„å—串,å—串ä¸çš„æ¯ä¸ªå—æ®µé—´ç”¨é€—å·åšåˆ†éš”符,第12ä¸ªå—æ®µç”±P开头。
æˆ‘ä»¬å®¹æ˜“æƒ³åˆ°è¿™æ ·çš„æ£åˆ™è¡¨è¾¾å¼<<^(.*?,){11}P>>。这个æ£åˆ™è¡¨è¾¾å¼åœ¨æ£å¸¸æƒ…况下工作的很好。但是在æžç«¯æƒ…况下,如果第12ä¸ªå—æ®µä¸æ˜¯ç”±P开头,则会å‘生ç¾é𾿀§çš„å›žæº¯ã€‚å¦‚è¦æœç´¢çš„å—串为“1,2,3,4,5,6,7,8,9,10,11,12,13â€ã€‚首先,æ£åˆ™è¡¨è¾¾å¼ä¸€ç›´æˆåŠŸåŒ¹é…直到第12个å—符。这时,å‰é¢çš„æ£åˆ™è¡¨è¾¾å¼æ¶ˆè€—çš„å—串为“1,2,3,4,5,6,7,8,9,10,11,â€ï¼Œåˆ°äº†ä¸‹ä¸€ä¸ªå—符,<<P>>å¹¶ä¸åŒ¹é…“12â€ã€‚所以引擎进行回溯,这时æ£åˆ™è¡¨è¾¾å¼æ¶ˆè€—çš„å—串为“1,2,3,4,5,6,7,8,9,10,11â€ã€‚ç»§ç»ä¸‹ä¸€æ¬¡åŒ¹é…过程,下一个æ£åˆ™ç¬¦å·ä¸ºç‚¹å·<<.>>,å¯ä»¥åŒ¹é…下一个逗å·â€œ,â€ã€‚然而<<,>>å¹¶ä¸åŒ¹é…å—符“12â€ä¸çš„“1â€ã€‚匹é…失败,继ç»å›žæº¯ã€‚大家å¯ä»¥æƒ³è±¡ï¼Œè¿™æ ·çš„å›žæº¯ç»„åˆæ˜¯ä¸ªéžå¸¸å¤§çš„æ•°é‡ã€‚å› æ¤å¯èƒ½ä¼šé€ æˆå¼•擎崩溃。
用于阻æ¢è¿™æ ·å·¨å¤§çš„å›žæº¯æœ‰å‡ ç§æ–¹æ¡ˆï¼š
一ç§ç®€å•的方案是尽å¯èƒ½çš„使匹é…精确。用å–åå—符集代替点å·ã€‚例如我们用如下æ£åˆ™è¡¨è¾¾å¼<<^([^,\r\n]*,){11}P>>ï¼Œè¿™æ ·å¯ä»¥ä½¿å¤±è´¥å›žæº¯çš„æ¬¡æ•°ä¸‹é™åˆ°11次。
å¦ä¸€ç§æ–¹æ¡ˆæ˜¯ä½¿ç”¨åŽŸå组。
原å组的目的是使æ£åˆ™å¼•æ“Žå¤±è´¥çš„æ›´å¿«ä¸€ç‚¹ã€‚å› æ¤å¯ä»¥æœ‰æ•ˆçš„é˜»æ¢æµ·é‡å›žæº¯ã€‚原åç»„çš„è¯æ³•是<<(?>æ£åˆ™è¡¨è¾¾å¼)>>。ä½äºŽ(?>)之间的所有æ£åˆ™è¡¨è¾¾å¼éƒ½ä¼šè¢«è®¤ä¸ºæ˜¯ä¸€ä¸ªå•一的æ£åˆ™ç¬¦å·ã€‚一旦匹é…失败,引擎将会回溯到原å组å‰é¢çš„æ£åˆ™è¡¨è¾¾å¼éƒ¨åˆ†ã€‚å‰é¢çš„例å用原å组å¯ä»¥è¡¨è¾¾æˆ<<^(?>(.*?,){11})P>>。一旦第åäºŒä¸ªå—æ®µåŒ¹é…失败,引擎回溯到原å组å‰é¢çš„<<^>>。
Â
14. å‘剿Ÿ¥çœ‹ä¸Žå‘åŽæŸ¥çœ‹
Perl 5 引入了两个强大的æ£åˆ™è¯æ³•:“å‘剿Ÿ¥çœ‹â€å’Œâ€œå‘åŽæŸ¥çœ‹â€ã€‚他们也被称作“零长度æ–言â€ã€‚ä»–ä»¬å’Œé”šå®šä¸€æ ·éƒ½æ˜¯é›¶é•¿åº¦çš„ï¼ˆæ‰€è°“é›¶é•¿åº¦å³æŒ‡è¯¥æ£åˆ™è¡¨è¾¾å¼ä¸æ¶ˆè€—被匹é…çš„å—符串)。ä¸åŒä¹‹å¤„在于“å‰åŽæŸ¥çœ‹â€ä¼šå®žé™…匹é…å—ç¬¦ï¼Œåªæ˜¯ä»–们会抛弃匹é…åªè¿”回匹é…ç»“æžœï¼šåŒ¹é…æˆ–ä¸åŒ¹é…。这就是为什么他们被称作“æ–言â€ã€‚他们并ä¸å®žé™…消耗å—符串ä¸çš„å—ç¬¦ï¼Œè€Œåªæ˜¯æ–è¨€ä¸€ä¸ªåŒ¹é…æ˜¯å¦å¯èƒ½ã€‚
å‡ ä¹Žæœ¬æ–‡è®¨è®ºçš„æ‰€æœ‰æ£åˆ™è¡¨è¾¾å¼çš„实现都支æŒâ€œå‘å‰å‘åŽæŸ¥çœ‹â€ã€‚唯一的一个例外是Javascriptåªæ”¯æŒå‘剿Ÿ¥çœ‹ã€‚
·       肯定和å¦å®šå¼çš„å‘剿Ÿ¥çœ‹
如我们å‰é¢æè¿‡çš„一个例åï¼šè¦æŸ¥æ‰¾ä¸€ä¸ªq,åŽé¢æ²¡æœ‰ç´§è·Ÿä¸€ä¸ªu。也就是说,è¦ä¹ˆqåŽé¢æ²¡æœ‰å—符,è¦ä¹ˆåŽé¢çš„å—ç¬¦ä¸æ˜¯u。采用å¦å®šå¼å‘剿Ÿ¥çœ‹åŽçš„一个解决方案为<<q(?!u)>>。å¦å®šå¼å‘剿Ÿ¥çœ‹çš„è¯æ³•是<<(?!查看的内容)>>。
肯定å¼å‘剿Ÿ¥çœ‹å’Œå¦å®šå¼å‘剿Ÿ¥çœ‹å¾ˆç±»ä¼¼ï¼š<<(?=查看的内容)>>。
如果在“查看的内容â€éƒ¨åˆ†æœ‰ç»„,也会产生一个å‘åŽå¼•用。但是å‘剿Ÿ¥çœ‹æœ¬èº«å¹¶ä¸ä¼šäº§ç”Ÿå‘åŽå¼•用,也ä¸ä¼šè¢«è®¡å…¥å‘åŽå¼•用的编å·ä¸ã€‚è¿™æ˜¯å› ä¸ºå‘剿Ÿ¥çœ‹æœ¬èº«æ˜¯ä¼šè¢«æŠ›å¼ƒæŽ‰çš„,åªä¿ç•™åŒ¹é…与å¦çš„判æ–ç»“æžœã€‚å¦‚æžœä½ æƒ³ä¿ç•™åŒ¹é…的结果作为å‘åŽå¼•ç”¨ï¼Œä½ å¯ä»¥ç”¨<<(?=(regex))>>æ¥äº§ç”Ÿä¸€ä¸ªå‘åŽå¼•用。
·       肯定和å¦å®šå¼çš„å…ˆåŽæŸ¥çœ‹
å‘åŽæŸ¥çœ‹å’Œå‘剿Ÿ¥çœ‹æœ‰ç›¸åŒçš„æ•ˆæžœï¼Œåªæ˜¯æ–¹å‘相å
å¦å®šå¼å‘åŽæŸ¥çœ‹çš„è¯æ³•是:<<(?<!查看内容)>>
肯定å¼å‘åŽæŸ¥çœ‹çš„è¯æ³•是:<<(?<=查看内容)>>
我们å¯ä»¥çœ‹åˆ°ï¼Œå’Œå‘剿Ÿ¥çœ‹ç›¸æ¯”,多了一个表示方å‘的左尖括å·ã€‚
例:<<(?<!a)b>>将会匹é…一个没有“aâ€ä½œå‰å¯¼å—符的“bâ€ã€‚
值得注æ„的是:å‘剿Ÿ¥çœ‹ä»Žå½“å‰å—符串ä½ç½®å¼€å§‹å¯¹â€œæŸ¥çœ‹â€æ£åˆ™è¡¨è¾¾å¼è¿›è¡ŒåŒ¹é…ï¼›å‘åŽæŸ¥çœ‹åˆ™ä»Žå½“å‰å—符串ä½ç½®å¼€å§‹å…ˆåŽå›žæº¯ä¸€ä¸ªå—符,然åŽå†å¼€å§‹å¯¹â€œæŸ¥çœ‹â€æ£åˆ™è¡¨è¾¾å¼è¿›è¡ŒåŒ¹é…。
Â
·       深入æ£åˆ™è¡¨è¾¾å¼å¼•擎内部
让我们看一个简å•例å。
把æ£åˆ™è¡¨è¾¾å¼<<q(?!u)>>应用到å—符串“Iraqâ€ã€‚æ£åˆ™è¡¨è¾¾å¼çš„ç¬¬ä¸€ä¸ªç¬¦å·æ˜¯<<q>>。æ£å¦‚我们知é“的,引擎在匹é…<<q>>以å‰ä¼šæ‰«è¿‡æ•´ä¸ªå—符串。当第四个å—符“qâ€è¢«åŒ¹é…åŽï¼Œâ€œqâ€åŽé¢æ˜¯ç©ºå—符(void)。而下一个æ£åˆ™ç¬¦å·æ˜¯å‘剿Ÿ¥çœ‹ã€‚引擎注æ„到已ç»è¿›å…¥äº†ä¸€ä¸ªå‘剿Ÿ¥çœ‹æ£åˆ™è¡¨è¾¾å¼éƒ¨åˆ†ã€‚下一个æ£åˆ™ç¬¦å·æ˜¯<<u>>,和空å—符ä¸åŒ¹é…,从而导致å‘剿Ÿ¥çœ‹é‡Œçš„æ£åˆ™è¡¨è¾¾å¼åŒ¹é…å¤±è´¥ã€‚å› ä¸ºæ˜¯ä¸€ä¸ªå¦å®šå¼çš„å‘剿Ÿ¥çœ‹ï¼Œæ„å‘³ç€æ•´ä¸ªå‘剿Ÿ¥çœ‹ç»“果是æˆåŠŸçš„ã€‚äºŽæ˜¯åŒ¹é…结果“qâ€è¢«è¿”回了。
我们在把相åŒçš„æ£åˆ™è¡¨è¾¾å¼åº”用到“quitâ€ã€‚<<q>>匹é…了“qâ€ã€‚下一个æ£åˆ™ç¬¦å·æ˜¯å‘剿Ÿ¥çœ‹éƒ¨åˆ†çš„<<u>>,它匹é…了å—符串ä¸çš„第二个å—符“iâ€ã€‚引擎继ç»èµ°åˆ°ä¸‹ä¸ªå—符“iâ€ã€‚然而引擎这时注æ„到å‘剿Ÿ¥çœ‹éƒ¨åˆ†å·²ç»å¤„ç†å®Œäº†ï¼Œå¹¶ä¸”å‘剿Ÿ¥çœ‹å·²ç»æˆåŠŸã€‚äºŽæ˜¯å¼•æ“ŽæŠ›å¼ƒè¢«åŒ¹é…çš„å—符串部分,这将导致引擎回退到å—符“uâ€ã€‚
å› ä¸ºå‘剿Ÿ¥çœ‹æ˜¯å¦å®šå¼çš„,æ„å‘³ç€æŸ¥çœ‹éƒ¨åˆ†çš„æˆåŠŸåŒ¹é…导致了整个å‘剿Ÿ¥çœ‹çš„å¤±è´¥ï¼Œå› æ¤å¼•擎ä¸å¾—ä¸è¿›è¡Œå›žæº¯ã€‚最åŽå› ä¸ºå†æ²¡æœ‰å…¶ä»–的“qâ€å’Œ<<q>>匹é…,所以整个匹é…失败了。
为了确ä¿ä½ 能清楚地ç†è§£å‘剿Ÿ¥çœ‹çš„实现,让我们把<<q(?=u)i>>应用到“quitâ€ã€‚<<q>>首先匹é…“qâ€ã€‚ç„¶åŽå‘剿Ÿ¥çœ‹æˆåŠŸåŒ¹é…“uâ€ï¼ŒåŒ¹é…的部分被抛弃,åªè¿”回å¯ä»¥åŒ¹é…的判æ–结果。引擎从å—符“iâ€å›žé€€åˆ°â€œuâ€ã€‚由于å‘剿Ÿ¥çœ‹æˆåŠŸäº†ï¼Œå¼•æ“Žç»§ç»å¤„ç†ä¸‹ä¸€ä¸ªæ£åˆ™ç¬¦å·<<i>>。结果å‘现<<i>>和“uâ€ä¸åŒ¹é…ã€‚å› æ¤åŒ¹é…失败了。由于åŽé¢æ²¡æœ‰å…¶ä»–的“qâ€ï¼Œæ•´ä¸ªæ£åˆ™è¡¨è¾¾å¼çš„匹é…失败了。
Â
·       更进一æ¥ç†è§£æ£åˆ™è¡¨è¾¾å¼å¼•擎内部机制
让我们把<<(?<=a)b>>应用到“thingamabobâ€ã€‚引擎开始处ç†å‘åŽæŸ¥çœ‹éƒ¨åˆ†çš„æ£åˆ™ç¬¦å·å’Œå—符串ä¸çš„第一个å—符。在这个例åä¸ï¼Œå‘åŽæŸ¥çœ‹å‘Šè¯‰æ£åˆ™è¡¨è¾¾å¼å¼•擎回退一个å—ç¬¦ï¼Œç„¶åŽæŸ¥çœ‹æ˜¯å¦æœ‰ä¸€ä¸ªâ€œaâ€è¢«åŒ¹é…ã€‚å› ä¸ºåœ¨â€œtâ€å‰é¢æ²¡æœ‰å—符,所以引擎ä¸èƒ½å›žé€€ã€‚å› æ¤å‘åŽæŸ¥çœ‹å¤±è´¥äº†ã€‚引擎继ç»èµ°åˆ°ä¸‹ä¸€ä¸ªå—符“hâ€ã€‚å†ä¸€æ¬¡ï¼Œå¼•擎暂时回退一个å—ç¬¦å¹¶æ£€æŸ¥æ˜¯å¦æœ‰ä¸ªâ€œaâ€è¢«åŒ¹é…。结果å‘现了一个“tâ€ã€‚å‘åŽæŸ¥çœ‹åˆå¤±è´¥äº†ã€‚
å‘åŽæŸ¥çœ‹ç»§ç»å¤±è´¥ï¼Œç›´åˆ°æ£åˆ™è¡¨è¾¾å¼åˆ°è¾¾äº†å—符串ä¸çš„“mâ€ï¼ŒäºŽæ˜¯è‚¯å®šå¼çš„å‘åŽæŸ¥çœ‹è¢«åŒ¹é…äº†ã€‚å› ä¸ºå®ƒæ˜¯é›¶é•¿åº¦çš„ï¼Œå—符串的当å‰ä½ç½®ä»ç„¶æ˜¯â€œmâ€ã€‚下一个æ£åˆ™ç¬¦å·æ˜¯<<b>>,和“mâ€åŒ¹é…失败。下一个å—符是å—符串ä¸çš„第二个“aâ€ã€‚引擎å‘åŽæš‚æ—¶å›žé€€ä¸€ä¸ªå—符,并且å‘现<<a>>ä¸åŒ¹é…“mâ€ã€‚
在下一个å—符是å—符串ä¸çš„第一个“bâ€ã€‚引擎暂时性的å‘åŽé€€ä¸€ä¸ªå—符å‘现å‘åŽæŸ¥çœ‹è¢«æ»¡è¶³äº†ï¼ŒåŒæ—¶<<b>>匹é…了“bâ€ã€‚å› æ¤æ•´ä¸ªæ£åˆ™è¡¨è¾¾å¼è¢«åŒ¹é…了。作为结果,æ£åˆ™è¡¨è¾¾å¼è¿”回å—符串ä¸çš„第一个“bâ€ã€‚
·       å‘å‰å‘åŽæŸ¥çœ‹çš„应用
我们æ¥çœ‹è¿™æ ·ä¸€ä¸ªä¾‹å:查找一个具有6ä½å—ç¬¦çš„ï¼Œå«æœ‰â€œcatâ€çš„å•è¯ã€‚
首先,我们å¯ä»¥ä¸ç”¨å‘å‰å‘åŽæŸ¥çœ‹æ¥è§£å†³é—®é¢˜ï¼Œä¾‹å¦‚:
<< cat\w{3}|\wcat\w{2}|\w{2}cat\w|\w{3}cat>>
足够简å•å§ï¼ä½†æ˜¯å½“éœ€æ±‚å˜æˆæŸ¥æ‰¾ä¸€ä¸ªå…·æœ‰6-12ä½å—ç¬¦ï¼Œå«æœ‰â€œcatâ€ï¼Œâ€œdogâ€æˆ–“mouseâ€çš„å•è¯æ—¶ï¼Œè¿™ç§æ–¹æ³•å°±å˜å¾—有些笨拙了。
我们æ¥çœ‹çœ‹ä½¿ç”¨å‘剿Ÿ¥çœ‹çš„æ–¹æ¡ˆã€‚在这个例åä¸ï¼Œæˆ‘ä»¬æœ‰ä¸¤ä¸ªåŸºæœ¬éœ€æ±‚è¦æ»¡è¶³ï¼šä¸€æ˜¯æˆ‘们需è¦ä¸€ä¸ª6ä½çš„å—符,二是å•è¯å«æœ‰â€œcatâ€ã€‚
满足第一个需求的æ£åˆ™è¡¨è¾¾å¼ä¸º<<\b\w{6}\b>>。满足第二个需求的æ£åˆ™è¡¨è¾¾å¼ä¸º<<\b\w*cat\w*\b>>。
把两者结åˆèµ·æ¥ï¼Œæˆ‘们å¯ä»¥å¾—到如下的æ£åˆ™è¡¨è¾¾å¼ï¼š
    <<(?=\b\w{6}\b)\b\w*cat\w*\b>>
具体的匹é…è¿‡ç¨‹ç•™ç»™è¯»è€…ã€‚ä½†æ˜¯è¦æ³¨æ„的一点是,å‘剿Ÿ¥çœ‹æ˜¯ä¸æ¶ˆè€—å—ç¬¦çš„ï¼Œå› æ¤å½“判æ–å•è¯æ»¡è¶³å…·æœ‰6个å—符的æ¡ä»¶åŽï¼Œå¼•擎会从开始判æ–å‰çš„ä½ç½®ç»§ç»å¯¹åŽé¢çš„æ£åˆ™è¡¨è¾¾å¼è¿›è¡ŒåŒ¹é…。
最åŽä½œäº›ä¼˜åŒ–,å¯ä»¥å¾—到下é¢çš„æ£åˆ™è¡¨è¾¾å¼ï¼š
<<\b(?=\w{6}\b)\w{0,3}cat\w*>>
Â
15. æ£åˆ™è¡¨è¾¾å¼ä¸çš„æ¡ä»¶æµ‹è¯•
æ¡ä»¶æµ‹è¯•çš„è¯æ³•为<<(?ifthen|else)>>。“ifâ€éƒ¨åˆ†å¯ä»¥æ˜¯å‘å‰å‘åŽæŸ¥çœ‹è¡¨è¾¾å¼ã€‚如果用å‘剿Ÿ¥çœ‹ï¼Œåˆ™è¯æ³•å˜ä¸ºï¼š<<(?(?=regex)then|else)>>,其ä¸else部分是å¯é€‰çš„。
如果if部分为true,则æ£åˆ™å¼•擎会试图匹é…then部分,å¦åˆ™å¼•擎会试图匹é…else部分。
需è¦è®°ä½çš„æ˜¯ï¼Œå‘å‰å…ˆåŽæŸ¥çœ‹å¹¶ä¸å®žé™…消耗任何å—ç¬¦ï¼Œå› æ¤åŽé¢çš„then与elseéƒ¨åˆ†çš„åŒ¹é…æ—¶ä»Žif测试å‰çš„部分开始进行å°è¯•。
Â
16. 为æ£åˆ™è¡¨è¾¾å¼æ·»åŠ æ³¨é‡Š
在æ£åˆ™è¡¨è¾¾å¼ä¸æ·»åŠ æ³¨é‡Šçš„è¯æ³•是:<<(?#comment)>>
ä¾‹ï¼šä¸ºç”¨äºŽåŒ¹é…æœ‰æ•ˆæ—¥æœŸçš„æ£åˆ™è¡¨è¾¾å¼æ·»åŠ æ³¨é‡Šï¼š
 (?#year)(19|20)\d\d[- /.](?#month)(0[1-9]|1[012])[- /.](?#day)(0[1-9]|[12][0-9]|3[01])