这篇文章适合你吗?
要读懂这篇文章的精髓,你最好要有一点正则匹配原理的基础。比如".*?"匹配文本内容"asp163",稍懂正则表达式的人都知道可以匹配,但是你知道他的匹配过程吗?如果你不太清楚,那么下面的内容,对你来说可能不太适合,或许,看的太吃力且无法领悟平衡组的用法。因此,我建议你先了解正则表达式NFA引擎的匹配原理。想要整理一份易懂易描述的话,的确要费些时间,但不知道这篇内容会不会达到我预期的效果。慢慢完善吧~(注:这是我2010年写的,现在拿过来,有时间将自己做为读者来看本篇文章,修改有问题的地方,并增加些实例,尽量做到通俗易懂。)
一般正则教程中对平衡组的介绍
如果想要匹配可嵌套的层次性结构的话,就得使用平衡组了。举个例子吧,如何把“xx
这里需要用到以下的语法构造:
(?
把捕获的内容命名为group,并压入堆栈
(&#63;<-group>)
从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
(&#63;(group)yes|no)
如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
(&#63;!)
顺序否定环视,由于没有后缀表达式,试图匹配总是失败
如果你不是一个程序员(或者你是一个对堆栈的概念不熟的程序员),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个(或再写一个)"group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配no部分。
我们需要做的是每碰到了左括号,就在黑板上写一个"group",每碰到一个右括号,就擦掉一个,到了最后就看看黑板上还有没有-如果有那就证明左括号比右括号多,那匹配就应该失败(为了能看得更清楚一点,我用了(&#63;'group')的语法):
<#最外层的左括号 [^<>]* #最外层的左括号后面的不是括号的内容 ( ( (&#63;'Open'<) #碰到了左括号,在黑板上写一个"Open" [^<>>]* #匹配左括号后面的不是括号的内容 )+ ( (&#63;'-Open'>) #碰到了右括号,擦掉一个"Open" [^<>]* #匹配右括号后面不是括号的内容 )+ )* (&#63;(Open)(&#63;!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果有,则匹配失败 > #最外层的右括号
我为什么写这篇文章
看了上面的介绍,你明白了吗?在我未理解正则表达式匹配原理之前,看上面对于平衡组的介绍,似懂非懂,且只能当做模板记住,而不能灵活运用。因此查阅大量有关正则方面的资料,这里尤其感谢lxcnn的技术文档及《精通正则表达式》这本书,让我对正则表达式有了更深入、更系统的理解,因此,在它们的基础之上,我就结合自己的学习经历做个小结,一来做为学习笔记存档,另外,如果能解决你的疑惑,也是件让人高兴的事。
我先暂不分析上面的代码,先讲解一下关于平衡组相关的概念及知识。
下面表达式匹配测试工具为:Expresso,本站也提供它的完美破解版下载。
平衡组的概念及作用
平衡组,故名思义,平衡即对称,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配。平衡组有狭义与广义两种定义,狭义平衡组指(&#63;Expression)
语法,而广义平衡组并不是固定的语法规则,而是几种语法规则的综合运用,我们平时所说的平衡组通常指的是广义平衡组。本文中如无特殊说明,平衡组这种简写指的是广义平衡组。
平衡组的匹配原理
平衡组的匹配原理可以用堆栈来解释,先举个例子,再根据例子进行解释。
源字符串:a+(b*(c+d))/e+f-(g/(h-i))*j
正则表达式:
((&#63;
需求说明:匹配成对出现的()中的内容
输出:(b*(c+d)) 和 (g/(h-i))
我将上面正则表达式代码分行写,并加上注释,这样看起来有层次,而且方便
\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (&#63;\() #命名捕获组,遇到开括弧“Open”计数加1 | #分支结构 (&#63;<-Open>\)) #狭义平衡组,遇到闭括弧“Open”计数减1 | #分支结构 [^()]+ #非括弧的其它任意字符 )* #以上子串出现0次或任意多次 (&#63;(Open)(&#63;!)) #判断是否还有“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))(红色显示的括号)——引擎此时将控制权交给(&#63;(Open)(&#63;!))
,判断堆栈中是否为0,如果为0,则执行匹配“no”分支,由于这个条件判断结构中没有“no”分支,所以什么都不做,把控制权交给接下来的“\)”
这个正则表达式“\)”可匹配接下来的),即b))(红色显示的括号)
4、后面的 (&#63;(Open)(&#63;!))
用来保证堆栈中Open捕获组计数是否为0,也就是“(”和“)”是配对出现的
5、最后的“)”,作为匹配的结束
匹配过程
首先匹配第一个“(”,然后一直匹配,直到出现以下两种情况之一时,把控制权交给(&#63;(Open)(&#63;!)):
a)堆栈中Open计数已为0,此时再遇到“)”
b)匹配到字符串结束符
这时控制权交给(&#63;(Open)(&#63;!))
,判断Open是否有匹配,由于此时计数为0,没有匹配,那么就匹配“no”分支,由于这个条件判断结构中没有“no”分支,所以什么都不做,把控制权交给接下来的“\)”
如果上面遇到的是情况a),那么此时“\)”可以匹配接下来的“)”,匹配成功;
如果上面遇到的是情况b),那么此时会进行回溯,直到“\)”匹配成功为止,否则报告整个表达式匹配失败。
由于.NET中的狭义平衡组“(&#63;
”结构,可以动态的对堆栈中捕获组进行计数,匹配到一个开始标记,入栈,计数加1,匹配到一个结束标记,出栈,计数减1,最后再判断堆栈中是否还有Open,有则说明开始和结束标记不配对出现,不匹配,进行回溯或报告匹配失败;如果没有,则说明开始和结束标记配对出现,继续进行后面子表达式的匹配。
需要对“(&#63;!)
”进行一下说明,它属于顺序否定环视,完整的语法是“(&#63;!Expression)
”。由于这里的“Expression”不存在,表示这里不是一个位置,所以试图尝试匹配总是失败的,作用就是在Open不配对出现时,报告匹配失败。
下面在看个例子:
|
以上为部分的HTML代码.现在我们的问题是要提取出其
snhame 原因也很简单,它和离他最近的标签匹配上了,不过它不知道这个标签不是它的-_-,是不是就是&#63;符号的原因呢,我们去掉让他无限制贪婪,可这下问题更大了,什么乱七八糟的东东它都匹配到了
snhame f 这个结果也不是我们想要的。那么我就用“平衡组”来解决吧。
]*>((&#63; ]*>)+|(&#63;<-mm> )|[\s\S])*&#63;(&#63;(mm)(&#63;!))匹配的结果是
snhame f 这正是我们想要的
注意,我开始写成这样的方式]*>((&#63; ]*>)+|(&#63;<-mm> )|[\s\S])*(&#63;(mm)(&#63;!))匹配的结果是
snhame f 一个问题
以下代码只是做为一个问题探讨
文本内容:e+f(-(g/(h-i))*j正则表达式:
\( ( (&#63;\() | (&#63;<-mm>\)) | . )*&#63; (&#63;(mm)(&#63;!)) \) 匹配的结果是:(-(g/(h-i))
写下你的评论吧 !推荐阅读
本文探讨了如何像程序员一样思考,强调了将复杂问题分解为更小模块的重要性,并讨论了如何通过妥善管理和复用已有代码来提高编程效率。 ... [详细]蜡笔小新 2024-12-28 01:48:10 一位程序员的妻子在网上分享了她丈夫在北京工作八年的经历,月薪仅3万元,存款情况却出乎意料。本文探讨了高学历人才在大城市的职场现状及生活压力。 ... [详细]蜡笔小新 2024-12-28 11:14:15 本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]蜡笔小新 2024-12-28 11:52:00 本文详细介绍如何从官方渠道下载并安装PyCharm集成开发环境(IDE),涵盖Windows、macOS和Linux系统,同时提供详细的安装步骤及配置建议。 ... [详细]蜡笔小新 2024-12-28 09:42:41 本文总结了2018年的关键成就,包括职业变动、购车、考取驾照等重要事件,并分享了读书、工作、家庭和朋友方面的感悟。同时,展望2019年,制定了健康、软实力提升和技术学习的具体目标。 ... [详细]蜡笔小新 2024-12-28 09:10:26 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]蜡笔小新 2024-12-28 08:20:07 本周信息安全小组主要进行了CTF竞赛相关技能的学习,包括HTML和CSS的基础知识、逆向工程的初步探索以及整数溢出漏洞的学习。此外,还掌握了Linux命令行操作及互联网工作原理的基本概念。 ... [详细]蜡笔小新 2024-12-28 05:52:22 本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]蜡笔小新 2024-12-28 04:11:47 本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]蜡笔小新 2024-12-28 02:40:28 本文详细介绍了如何解决Uploadify插件在Internet Explorer(IE)9和10版本中遇到的点击失效及JQuery运行时错误问题。通过修改相关JavaScript代码,确保上传功能在不同浏览器环境中的一致性和稳定性。 ... [详细]蜡笔小新 2024-12-27 22:07:40 本文介绍了如何利用JavaScript或jQuery来判断网页中的文本框是否处于焦点状态,以及如何检测鼠标是否悬停在指定的HTML元素上。 ... [详细]蜡笔小新 2024-12-27 21:33:33 在命令行模式下敲命令python,就看到类似如下的一堆文本输出,然后就进入到Python交互模式,它的提示符是>>>,此时我们可以使用print() ... [详细]蜡笔小新 2024-12-27 21:32:05 本题涉及编号为1至n的火星商店,每个商店有一个永久商品价值v。操作包括每天在指定商店增加一个新商品,以及查询某段时间内某些商店中所有商品(含永久商品)与给定密码值的最大异或结果。通过线段树分治和持久化Trie树来高效解决此问题。 ... [详细]蜡笔小新 2024-12-27 21:23:11 王静芸平桂这个家伙很懒,什么也没留下!Tags | 热门标签RankList | 热门文章
- 1搭建gitolite服务器
- 2你在享受十一长假时,Python 已悄悄地变了!
- 3炎症与大脑
- 4如何恢复CMOS出厂设置?
- 5Java web 开发填坑记 1 -如何正确的下载 eclipse
- 6贪心算法 哈夫曼树编码
- 7扩展名详解(转载)
- 8如何更好的释放Mac空间之技巧篇
- 9Map旗下映射类总结
- 10Linux ext2, ext3, ext4 文件系统解读[4]
- 11Spring Security 添加记住我功能(转载)
- 12Shell脚本(for循环,while循环,break跳出循环,continue结束本次循环)
- 13查找包含特定值的数组的文档
- 14用命令实现Win7远程桌面关机和重启
- 15动态设置Feign的Header信息
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有