本帖最后由 liu-aguang 于 2014-6-17 07:55 编辑
第四篇 技巧篇 文本处理归根到底是对特定串的捕捉(查找),所以我们讨论的所谓”技巧”聚焦于对特定字符串的描述.而现实中待处理的字符串在目标文本中的存在状态形形色色,与大多数文章以文本类型归类不同,本篇根据字符串结构特征分类进行讨论,试图从正则特性角度帮助初学者提高综合应用正则符号的能力.文中例举的是常见任务类型或任务的一部分,我们精选了一些较短小的,”天才”般构建的正则表达式进行讨论,相信通过你的仔细解读,一定会心有所悟,提升编写正则表达式的思维境界.也欢迎朋友们跟贴,把你所见所写的自认为有新意的正则表达式展示出来,供大家学习讨论.本篇的正则表达式有的来自世界级专家,也有的来自网络网友,在此一并致谢! 一. 匹配具有多种形态结构的字符串
在现实的世界中,有些数据类型具有多种呈现形式,如HTML标签,它可能有属性也可能没有,在一些位置上可能有若干空格也可能没有等等. 下面来讨论几个实例: 1. 匹配下列文本中的<HR>标签,它可能呈现的形式例举如下: <HR> <HR > <HR size = 14 > 正则表达式: <HR(\s+size\s*=\s*\d+)?\s*> 字符串中,”<HR”与”>”是标签的标志,是肯定出现的,所以用字面字符表示; 它的size属性可能出现也可能不出现,可用”?”描述,属性是一个相对独立的整体所以用括号包围,(…)?表示要么整体出现,要么都不出现;标签与属性之间至少必须有一个空格,用\+表示;而其它有些地方可以没有空格也可以有多空格用\s*表示. 2. 匹配浮点数,它可能有下列几种呈现形式: -56 0 +0.14 35.699 .123 正则表达式: [+-]?\d+(\.\d+)?|\.\d+ +或-可用字符组[+-];它们可有可无可用[+-]?; 整数部分部分可用\d+ 小数部分可用\.\d+ ,小数部分可有可无,可用可选项元字符?,由于小数部分如果出现则小数点与后面的数据将同时出现,反之,同时不出现,所以用分组括号包围让?号整体作用即:(?:\.\d+)? 于是整合起来就是[+-]?\d+(?:\.\d+)? 整数部分也不是必须出现的,即实际浮点数表示法,可以存在只是小数部分也合法.能否用[+-]?\d*\(\.\d+)?表示各种情形呢?这是不行的.因为在这个表达式中所有子表达式都可以不出现,就意味着什么都不匹配也匹配成功.也意味着什么都可以匹配. 它的解决办法是用选择符把它们作为两部分分别匹配两种可能的情况.即 [+-]?\d+(\.\d+)?|\.\d+ 在这里选择分支的前后顺序很重要,不能交换,你知道为什么吗?如果不明白建议回头再研究一下原理. 3. 匹配某范围内的数据 有时候我们需要匹配一个数值范围,如一天内的小时数,一月(年)内的天数,等等.遗憾的是正则表达式的元字符(序列)里没有专门用来表示数据范围的符号. 在正则中想要匹配包含多于一个数字的整数,就必须罗列出所有的数字组合.先看几个例子: 1-12 正则表达式: ^(1[0-2]|[1-9])$ 1-22范围内有,1, 2, 3, ...12共有12个数字,事实上我们可以用多选结构把它们连接起来:1|2|...|12, 显然这不是我们需要的方案. 我们把1-12分为两部分:1-9和10-12. 第一部分可用字符组[1-9];第二部分可用表达式1[0-2],然后用多选结构组合起来,即实现了表示1-12范围内的数值.这时也要注意子表达式在多选结构中的顺序. 1-31 正则表达式: ^3[01]|[12][0-9]|[1-9]$ 同样的思路把数值范围分为了三个部分,然后用多选结构组合起来,朋友们可自己去分析. 0-100 ^100|[1-9][0-9]?$ 在这里巧妙地用了一个可选项元素?,令第二个子表达式即可表示两位数字的数值,也可以表示只有一位数字的数值.这个表达式也可以写为 ^100|[1-9][0-9]|[0-9] 从前面原理部分知道,多选结构采用的是回溯算法,所以这个表达式的效率不如用可选项元素?的效率高. 0-255 正则表达式: ^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d$ 这是一个IP4地址的各组数据允许的范围.我们把它分为四个独立的部分:第一25[0-5]表示250-255;第二2[0-4]\d表示200-249;第三1\d{2}表示100-199;第四[1-9]?\d表示0-9和10-99 讨论: (1) 表示多个字符之一时用字符组,表示多个字符串之一时用多选结构,表示可出现可不出现用可选项元素,表示可出现多次时用”+”或”*”. 所以在表示具有多种可能呈现状态的复杂字符串时,一般方法是把它们分组,然后选择字符组和量词连结起来;事实上,当在现实的世界中有较多的问题都可以把它转化为这种匹配类型. (2) 一个正则表达式中,是不允许所有子表达式都是可选的情况.解决的方法是用多选结构把它们分为独立的多项. (3) 使用正则表达式来匹配整数区间的所有技巧:你只需要对区间进行简单拆分,直到拆分之后的所有区间都只包含固定个数的彼此无关的数字为止.这个拆分思维很重要,在以后的技巧中我们还会遇到.
补充内容 (2014-6-25 19:27):
在叙述"匹配浮点数"时,原文中,关于选择分支顺序的表述有问题.就匹配正确性来说,这个例子的选择分支前后顺序无关.但根据上下文环境,前后顺序可能会影响匹配效率.
补充内容 (2016-6-8 08:44):
0-100的正则就为^100|[1-9]?[0-9]$ |