ホームページ > 記事 > ウェブフロントエンド > バランスの取れたグループ正規表現の詳細な説明
この記事は主に 正規表現 を紹介します - バランスのとれたグループの詳細な説明です
この記事はあなたに適していますか?
この記事の本質を理解するには、通常のマッチング原則の基礎を理解しておく必要があります。たとえば、「.*?」はテキスト コンテンツ「asp163」と一致します。正規表現について少し知っている人なら誰でもそれが一致することを知っていますが、その一致プロセスを知っていますか?これについてよく理解していない場合は、次の内容が適していない可能性があります。おそらく、内容が難しすぎて、バランス グループの使用法が理解できない可能性があります。したがって、まず正規表現 NFA エンジンのマッチング原理を理解することをお勧めします。分かりやすい説明をまとめるのに少し時間がかかりますが、この内容で期待した効果が得られるかはわかりません。ゆっくり改善してください~ (注: これは 2010 年に書きました。時間があるときに、読者として読んでください。問題のある部分を修正し、いくつかの例を追加して、できるだけ理解しやすいようにします。)
一般的な通常のチュートリアルでのバランスのとれたグループの紹介
ネスト可能な階層構造に一致させたい場合は、バランスのとれたグループを使用する必要があります。たとえば、「xx 6ba7c8bc333a0dc8131222623db872d8 a2b23c6f9f7d4cb140313dcc8b2d4919 aa> yy」のような string 内の最長の山括弧内のコンテンツをキャプチャするにはどうすればよいでしょうか?
ここでは次の構文構造を使用する必要があります: (?1cd70caa06cc6f4a046ab31342a5e7b4)
キャプチャしたコンテンツにグループという名前を付け、スタックにプッシュします(?1cd70caa06cc6f4a046ab31342a5e7b4)
把捕获的内容命名为group,并压入堆栈(?16201dcfc173bb9cbaf05ae47be6aac7)
从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败(?(group)yes|no)
如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分(?!)
顺序否定环视,由于没有后缀表达式,试图匹配总是失败
如果你不是一个程序员(或者你是一个对堆栈的概念不熟的程序员),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个(或再写一个)"group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配no部分。
我们需要做的是每碰到了左括号,就在黑板上写一个"group",每碰到一个右括号,就擦掉一个,到了最后就看看黑板上还有没有-如果有那就证明左括号比右括号多,那匹配就应该失败(为了能看得更清楚一点,我用了(?'group')的语法):
< #最外层的左括号 [^<>]* #最外层的左括号后面的不是括号的内容 ( ( (?'Open'<) #碰到了左括号,在黑板上写一个"Open" [^<>>]* #匹配左括号后面的不是括号的内容 )+ ( (?'-Open'>) #碰到了右括号,擦掉一个"Open" [^<>]* #匹配右括号后面不是括号的内容 )+ )* (?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果有,则匹配失败 > #最外层的右括号
我为什么写这篇文章
看了上面的介绍,你明白了吗?在我未理解正则表达式匹配原理之前,看上面对于平衡组的介绍,似懂非懂,且只能当做模板记住,而不能灵活运用。因此查阅大量有关正则方面的资料,这里尤其感谢lxcnn的技术文档及《精通正则表达式》这本书,让我对正则表达式有了更深入、更系统的理解,因此,在它们的基础之上,我就结合自己的学习经历做个小结,一来做为学习笔记存档,另外,如果能解决你的疑惑,也是件让人高兴的事。
我先暂不分析上面的代码,先讲解一下关于平衡组相关的概念及知识。
下面表达式匹配测试工具为:Expresso,本站也提供它的完美破解版下载。
平衡组的概念及作用
平衡组,故名思义,平衡即对称,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配。平衡组有狭义与广义两种定义,狭义平衡组指(?Expression)
语法,而广义平衡组并不是固定的语法规则,而是几种语法规则的综合运用,我们平时所说的平衡组通常指的是广义平衡组。本文中如无特殊说明,平衡组这种简写指的是广义平衡组。
平衡组的匹配原理
平衡组的匹配原理可以用堆栈来解释,先举个例子,再根据例子进行解释。
源字符串:a+(b*(c+d))/e+f-(g/(h-i))*j<br>
正则表达式:((?aa2be2e892a0a40b70815c89ad50a9c9()|(?4ef86530cea17f4c1dd95ba9f67c8a9c)|[^()])*(?(Open)(?!)))
需求说明:匹配成对出现的()中的内容
输出:(b*(c+d)) 和 (g/(h-i))
(?16201dcfc173bb9cbaf05ae47be6aac7 )
最後にスタックにプッシュされたグループという名前のキャプチャされたコンテンツをスタックからポップします。スタックが元々空の場合、このグループのマッチングは失敗します(?(group)yes|no)
スタック上に group という名前のキャプチャされたコンテンツがある場合は、式の Yes 部分との一致を継続し、それ以外の場合は no 部分との一致を継続します
(?!)
そこからのシーケンスは探索を無効にします。は接尾辞式ではありません。合計を一致させようとしてください。これは失敗です あなたがプログラマーでない場合 (またはスタックの概念に慣れていないプログラマーである場合)、上記の 3 つの構文は次のように理解できます。 1 つ目は黒板に「グループ」を書く (または別の 1 つを書く)、2 つ目は黒板から「グループ」を消す、そして 3 つ目は黒板にまだ「グループ」が書かれているかどうかを確認することです。存在する場合は、引き続き「はい」部分と一致し、そうでない場合は「いいえ」部分と一致します。 私たちがしなければならないことは、左括弧に遭遇するたびに、黒板に「グループ」と書き、右括弧に遭遇するたびに、最後に黒板に何か残っているかどうかを確認します。左括弧が右括弧より多い場合、一致は失敗するはずです (より明確に見るために、(?'group') の構文を使用しました):
\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组,遇到开括弧“Open”计数加1 | #分支结构 (?<-Open>\)) #狭义平衡组,遇到闭括弧“Open”计数减1 | #分支结构 [^()]+ #非括弧的其它任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有“Open”,有则说明不配对,什么都不匹配 \) #普通闭括弧🎜🎜なぜ書いたかこの記事🎜🎜🎜上記の「はじめに」を読んで、理解できましたか?正規表現マッチングの原理を理解する前に、上記のバランスドグループの紹介を見ると、分かるようで理解できず、テンプレートとしてしか覚えられず、柔軟に活用できませんでした。そのため、正規表現についての情報をたくさん読みました。特に、正規表現をより深く体系的に理解できるようになった lxcnn の技術ドキュメントと書籍「Mastering Regular Expressions」に感謝しています。以上、私自身の学習経験をもとにまとめてみましたので、まずは学習ノートとして残しておきますので、疑問点が解決できれば幸いです。 🎜今回は上記のコードの分析は行いませんが、まずはバランスグループに関する概念や知識について説明します。 🎜次の表現一致テスト ツールは Expresso です。このサイトでは、ダウンロード用の完璧なクラック バージョンも提供しています。 🎜🎜🎜バランスグループの概念と機能🎜🎜🎜バランスグループは、その名前が示すように、主にいくつかの正規の文法規則を組み合わせて、ペアで現れる入れ子構造のマッチングを提供します。バランスグループには狭義と広義の 2 つの定義があります。狭義のバランスグループは
(?Expression)
文法を指しますが、広義のバランスグループは固定的な文法規則ではなく包括的なものです。いくつかの文法規則を適用することで、通常、いわゆるバランスのとれたグループは、一般化されたバランスのとれたグループを指します。この記事で特に指定がない限り、バランス グループの略語は一般化されたバランス グループを指します。 🎜バランス型グループのマッチング原理🎜 バランス型グループのマッチング原理は、最初に例を挙げ、次にその例に基づいて説明します。 🎜🎜ソース文字列: a+(b*(c+d))/e+f-(g/(h-i))*j🎜
正規表現: ((?7585a4a6e67c7bd5e7c800c50b61347b ()|(?4ef86530cea17f4c1dd95ba9f67c8a9c)|[^()])*(?(Open)(?!)))
🎜要件の説明: () のペアのコンテンツと一致します🎜出力: (b*(c+d)) and (g/(h-i))🎜上記の正規表現コードを別々の行に記述し、階層的に見えるようにコメントを追加しました🎜<table> <tr> <td id="td1"> </td> <td id="td2"> <table> <tr> <td>snhame</td> <td>f</td> </tr> </table> </td> <td></td> </tr> </table>🎜ネストされたコードの場合この例では、開始タグと終了タグが決定され、次のステップでは中間の文字が 3 つのカテゴリに分けられます。 1 つは「(」、もう 1 つは「)」で、残りはこれら 2 文字以外の任意の文字です。 🎜🎜すると、バランスグループのマッチング原理は次のようになります🎜
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中的狭义平衡组“(?08fe5173bd2246255fe28549c194ac4dExpression)
”结构,可以动态的对堆栈中捕获组进行计数,匹配到一个开始标记,入栈,计数加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代码.现在我们的问题是要提取出其100ccdc5f30895b24b92aa80cfaf494a的b6c5a531a458a2e790c1fd6421739d1c标签并将其删除掉,以往我们惯用的方法都是直接去取,像662e608096e8700eb640d3508b1fe34b[\s\S]+?\b90dd5946f0946207856a8a37f441edf,不过问题出来了,我们提取到的不是我们想要的内容,而是
<td id="td2"> <table> <tr> <td>snhame</td>
原因也很简单,它和离他最近的b90dd5946f0946207856a8a37f441edf标签匹配上了,不过它不知道这个标签不是它的-_-,是不是就是?符号的原因呢,我们去掉让他无限制贪婪,可这下问题更大了,什么乱七八糟的东东它都匹配到了
<td id="td2"> <table> <tr> <td>snhame</td>f
这个结果也不是我们想要的。那么我就用“平衡组”来解决吧。
2a7c83b38cefcd86f65b797ba54aea81]*>((?57193b04c2b951b1e7837fcc75cc8e84cb495b13cb504cf067fedb8c6f66b697]*>)+|(?1942aa3f8d29e470cbc78e852cf446e9b90dd5946f0946207856a8a37f441edf)|[\s\S])*?(?(mm)(?!))b90dd5946f0946207856a8a37f441edf
匹配的结果是
<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 中国語 Web サイトの他の関連記事を参照してください。