이번에는 일반 규칙(코드 포함)에서 잔액 그룹 사용에 대한 자세한 설명을 가져왔습니다. 일반 잔액 그룹 사용 시 주의사항은 무엇인가요?
이 기사가 귀하에게 적합합니까?
이 기사의 본질을 이해하려면 정기적인 일치 원칙에 대한 기초를 갖추는 것이 좋습니다. 예를 들어 ".*?"는 텍스트 내용 "asp163"과 일치합니다. 정규식에 대해 조금 아는 사람은 이것이 일치할 수 있다는 것을 알고 있지만 일치 프로세스를 알고 있습니까? 이에 대해 명확하지 않은 경우 다음 내용이 귀하에게 적합하지 않을 수 있습니다. 아마도 읽기가 너무 어렵고 잔액 그룹의 사용법을 이해하지 못할 수도 있습니다. 따라서 먼저 정규식 NFA 엔진의 매칭 원리를 이해하는 것이 좋습니다. 이해하기 쉬운 설명을 정리하는 데 시간이 좀 걸리긴 하지만, 이 콘텐츠가 기대했던 효과를 얻을 수 있을지는 모르겠습니다. 천천히 개선해 보세요~ (참고: 2010년에 쓴 글입니다. 이제 시간 나실 때 가져가서 독자로서 읽어 보세요. 문제가 되는 부분을 수정하고, 최대한 이해하기 쉽게 예시를 추가해 보세요. . )
일반 정규 튜토리얼에서 균형 그룹 소개중첩 가능한 계층 구조를 일치시키려면 균형 그룹을 사용해야 합니다. 예를 들어, "xx
에서 가장 긴 꺾쇠 괄호 안에 콘텐츠를 캡처하는 방법은 무엇입니까? 여기에서는 다음 구문 구조를 사용해야 합니다.
(?<group>캡처된 콘텐츠의 이름을 그룹으로 지정하고 스택에 푸시합니다.<p style="text-align: left;"><code>(? 스택에서 마지막으로 스택에 푸시된 그룹이라는 캡처된 콘텐츠를 팝합니다. 스택이 원래 비어 있으면 이 그룹의 일치가 실패합니다<br><code>(?(group)yes|no) code>스택에 group이라는 캡처된 콘텐츠가 있는 경우 yes 부분의 표현을 계속 일치시키고, 그렇지 않으면 계속 no 부분을 일치시킵니다.<code>(?<group>)
把捕获的内容命名为group,并压入堆栈(?<-group>)
从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败(?(group)yes|no)
如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分(?!)
顺序否定环视,由于没有后缀表达式,试图匹配总是失败
如果你不是一个程序员(或者你是一个对堆栈的概念不熟的程序员),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个(或再写一个)"group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配no部分。
我们需要做的是每碰到了左括号,就在黑板上写一个"group",每碰到一个右括号,就擦掉一个,到了最后就看看黑板上还有没有-如果有那就证明左括号比右括号多,那匹配就应该失败(为了能看得更清楚一点,我用了(?'group')的语法):
< #最外层的左括号 [^<>]* #最外层的左括号后面的不是括号的内容 ( ( (?'Open'<) #碰到了左括号,在黑板上写一个"Open" [^<>>]* #匹配左括号后面的不是括号的内容 )+ ( (?'-Open'>) #碰到了右括号,擦掉一个"Open" [^<>]* #匹配右括号后面不是括号的内容 )+ )* (?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果有,则匹配失败 > #最外层的右括号
我为什么写这篇文章
看了上面的介绍,你明白了吗?在我未理解正则表达式匹配原理之前,看上面对于平衡组的介绍,似懂非懂,且只能当做模板记住,而不能灵活运用。因此查阅大量有关正则方面的资料,这里尤其感谢lxcnn的技术文档及《精通正则表达式》这本书,让我对正则表达式有了更深入、更系统的理解,因此,在它们的基础之上,我就结合自己的学习经历做个小结,一来做为学习笔记存档,另外,如果能解决你的疑惑,也是件让人高兴的事。
我先暂不分析上面的代码,先讲解一下关于平衡组相关的概念及知识。
下面表达式匹配测试工具为:Expresso,本站也提供它的完美破解版下载。
平衡组的概念及作用
平衡组,故名思义,平衡即对称,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配。平衡组有狭义与广义两种定义,狭义平衡组指(?Expression)
(?!)
이 시퀀스는 둘러보기를 무효화합니다. .접미사 표현이 없기 때문에 합계를 맞추는 것은 실패입니다
프로그래머가 아니더라도(또는 스택 개념에 익숙하지 않은 프로그래머라면) 위의 세 가지 구문은 다음과 같이 이해할 수 있습니다. 이: 첫 번째는 칠판에 하나를 쓰는 것(또는 다른 하나를 쓰는 것) "그룹", 두 번째는 칠판에서 "그룹"을 지우는 것, 세 번째는 여전히 "그룹"이 쓰여 있는지 확인하는 것입니다. 칠판이 있으면 계속해서 '예' 부분을 일치시키고, 그렇지 않으면 '아니요' 부분을 일치시킵니다.
\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组,遇到开括弧“Open”计数加1 | #分支结构 (?<-Open>\)) #狭义平衡组,遇到闭括弧“Open”计数减1 | #分支结构 [^()]+ #非括弧的其它任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有“Open”,有则说明不配对,什么都不匹配 \) #普通闭括弧
(?Expression)
문법을 의미하는 반면, 넓은 의미의 균형그룹은 고정된 문법 규칙이 아니라 포괄적인 의미입니다. 여러 문법 규칙의 적용 우리는 일반적으로 소위 균형 그룹은 일반적으로 일반화된 균형 그룹을 나타냅니다. 본 조에서 달리 명시하지 않는 한, 잔액군이라는 약어는 일반화된 잔액군을 의미합니다. 🎜균형그룹의 매칭 원리🎜균형그룹의 매칭 원리는 먼저 스택을 사용하여 설명할 수 있고, 그다음 예시를 바탕으로 설명할 수 있습니다. 🎜源字符串:a+(b*(c+d))/e+f-(g/(h-i))*j
正则表达式:((?<Open>\()|(?<−Open>)|[^()])*(?(Open)(?!))\)
需求说明:匹配成对出现的()中的内容
输出:(b*(c+d)) 和 (g/(h-i))
我将上面正则表达式代码分行写,并加上注释,这样看起来有层次,而且方便
\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组,遇到开括弧“Open”计数加1 | #分支结构 (?<-Open>\)) #狭义平衡组,遇到闭括弧“Open”计数减1 | #分支结构 [^()]+ #非括弧的其它任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有“Open”,有则说明不配对,什么都不匹配 \) #普通闭括弧
对于一个嵌套结构而言,开始和结束标记都是确定的,对于本例开始为“(”,结束为“)”,那么接下来就是考察中间的结构,中间的字符可以划分为三类,一类是“(”,一类是“)”,其余的就是除这两个字符以外的任意字符。
那么平衡组的匹配原理就是这样的
1、先找到第一个“(”,作为匹配的开始。即上面的第1行,匹配了:a+(b*(c+d))/e+f-(g/(h-i))*j (红色显示部分)
2、在第1步以后,每匹配到一个“(”,就入栈一个Open捕获组,计数加1
3、在第1步以后,每匹配到一个“)”,就出栈最近入栈的Open捕获组,计数减1
也就是讲,上面的第一行正则“\(”匹配了:a+(b*(c+d))/e+f-(g/(h-i))*j
(红色显示部分)
然后,匹配到c前面的“(”,此时,计数加1;继续匹配,匹配到d后面的“)”,计算减1;——注意喽:此时堆栈中的计数是0,正则还是会向前继续匹配的,但是,如果匹配到“)”的话,比如,这个例子中d))(红色显示的括号)——引擎此时将控制权交给(?(Open)(?!))
,判断堆栈中是否为0,如果为0,则执行匹配“no”分支,由于这个条件判断结构中没有“no”分支,所以什么都不做,把控制权交给接下来的“\)”
这个正则表达式“\)”可匹配接下来的),即b))(红色显示的括号)
4、后面的 (?(Open)(?!))
用来保证堆栈中Open捕获组计数是否为0,也就是“(”和“)”是配对出现的
5、最后的“)”,作为匹配的结束
匹配过程
首先匹配第一个“(”,然后一直匹配,直到出现以下两种情况之一时,把控制权交给(?(Open)(?!)):
a)堆栈中Open计数已为0,此时再遇到“)”
b)匹配到字符串结束符
这时控制权交给(?(Open)(?!))
,判断Open是否有匹配,由于此时计数为0,没有匹配,那么就匹配“no”分支,由于这个条件判断结构中没有“no”分支,所以什么都不做,把控制权交给接下来的“\)”
如果上面遇到的是情况a),那么此时“\)”可以匹配接下来的“)”,匹配成功;
如果上面遇到的是情况b),那么此时会进行回溯,直到“\)”匹配成功为止,否则报告整个表达式匹配失败。
由于.NET中的狭义平衡组“(?<Close-Open>Expression)
”结构,可以动态的对堆栈中捕获组进行计数,匹配到一个开始标记,入栈,计数加1,匹配到一个结束标记,出栈,计数减1,最后再判断堆栈中是否还有Open,有则说明开始和结束标记不配对出现,不匹配,进行回溯或报告匹配失败;如果没有,则说明开始和结束标记配对出现,继续进行后面子表达式的匹配。
需要对“(?!)
”进行一下说明,它属于顺序否定环视,完整的语法是“(?!Expression)
”。由于这里的“Expression”不存在,表示这里不是一个位置,所以试图尝试匹配总是失败的,作用就是在Open不配对出现时,报告匹配失败。
下面在看个例子:
<table> <tr> <td id="td1"> </td> <td id="td2"> <table> <tr> <td>snhame</td> <td>f</td> </tr> </table> </td> <td></td> </tr> </table>
以上为部分的HTML代码.现在我们的问题是要提取出其
<td id="td2"> <table> <tr> <td>snhame</td>
原因也很简单,它和离他最近的标签匹配上了,不过它不知道这个标签不是它的-_-,是不是就是?符号的原因呢,我们去掉让他无限制贪婪,可这下问题更大了,什么乱七八糟的东东它都匹配到了
<td id="td2"> <table> <tr> <td>snhame</td>f
这个结果也不是我们想要的。那么我就用“平衡组”来解决吧。
匹配的结果是
<td id="td2"> <table> <tr> <td>snhame</td>f
这正是我们想要的
注意,我开始写成这样的方式
<td\s*id="td2"[^>]*>((?<mm><td[^>]*>)+|(?<-mm></td>)|[\s\S])*(?(mm)(?!))</td>
匹配的结果是
<td id="td2"> <table> <tr> <td>snhame</td>f
一个问题
以下代码只是做为一个问题探讨
文本内容:e+f(-(g/(h-i))*j
正则表达式:
\( ( (?<mm>\() | (?<-mm>\)) | . )*? (?(mm)(?!)) \)
匹配的结果是:(-(g/(h-i))
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
위 내용은 정규식에서 잔액 그룹 사용에 대한 자세한 설명(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!