《精通正则表示式》第三版 1-4:egrep正则表示式的应用

匹配标识符

C语言标识符:合法的标识符是由字母、数字和下划线的组成的序列,而且必须由英文字母或下划线开头,不能以数字开头。
identifier.txt:

## 标识符:只包含数字,字母,下划线。但是不能以字母开头
xiaoming1 //是标识符
2xiaoming //标识符不能以数字开头
_xiaoming3 //是标识符
#xiaoming //不是标识符:c语言标识符只能字母数字下划线组成
xiaoming@ //不是标识符,同上

匹配上面的标识符:
命令: egrep "\<[a-zA-Z_][a-zA-Z0-9_]*\>" identifier.txt ,效果如下。
这里写图片描述
可以看到上面的标识符已经选出来,但是还是有一些不正确地方,就是#xiaomingxiaoming@ 这两行都选出来了,但是还是选出来了,这是因为单词分界符只认识字母和数字的起始位置有关。也就说#和@这两个符号单词分界符\<\>不认为它是单词的一部分。所以上面的#和@并没有标红。
这里我还没有想到有什么很通用的方法去掉这些字符。
下面仅仅对上面的文本例子做匹配,可以加入行开头元字符^,让该行必须以字母下划线开头,这样就可以过滤掉#xiaoming这一行。效果如下:
这里写图片描述
可以看到已经滤掉#xiaoming这一行了,但是xiaoming@这一行还没有过滤掉,我们可以在使用排除型字符组,使得单词分界结束符后面不能出现@这个字符,命令: egrep "^\<[a-zA-Z_][a-zA-Z0-9_]*\>[^#@].*$" identifier.txt效果如下。
这里写图片描述
但是这种修修补补的做法,不具备通用型,如果此时如果又来一个新的符号&
那上面的正则表达式就不好用了。
这里写图片描述

引号内的字符串

匹配引号内字字符串最简便的办法就是使用这个表达式"[^"]*",不过在使用的时候要注意使用转义符进行转义,不然egrep运行出错,命令:egrep "\"[^\"]*\"" str.txt .


实例:
str.txt:

private String str="小明";

命令:egrep "\"[^\"]*\"" str.txt,匹配结果如下。
这里写图片描述


表达式"[^"]*"中两端的引号用来匹配字符串的开头和结尾。在这两个引号之间的文本可以包括双引号之外的任何字符,所以用[^"]表示双引号之间的任何字符,用*来表示引号之间可以存在任意数目的非双引号字符。
不过关于引号字符串,双引号之间可以出现有转义之后的引号,也是就是\"这种形式的引号。例如String str2="{\"小明\",\"小李\",\"小王\"}";上面的正则表达式就不适用了,可以改成"([^"]|(\")?)*"这样的形式即可。


str2.java:

package lan.base;

public class Test
{
    public static void main(String[] args)
    {
        String str1="小明";
        String str2="{\"小明\",\"小李\",\"小王\"}";
        System.out.println(str2);
    }
}

命令:egrep "\"([^\"]|(\\\")?)*\"" str2.java,匹配效果如下。
这里写图片描述
可以看到我们实现了在字符串的匹配。即使引号里面有转义引号的情况。对不之前的正则表达式式:"[^"]*",我们可以看到区别:
这里写图片描述

应用3:匹配美元金额(可能是小数)

\$[0-9]+(\.[0-9][0-9])?是一种匹配美元金额的办法.
从整体上看,这个表达式很简单,分为三部分\$,[0-9]+(\.[0-9][0-9])?,大致可以理解为,一个美元符号,然后是一组数字,之后可能一组字符(由小数点和两组数字组成)

从几个方便来看,这表达式还是很简陋的。比如它只能接受 1000 1000 , 而 无 法 接 受 1,000。它确实能接受可能出现的小数部分


dollar.txt:

$123.12
$11.00
$11
$1000
$1,000

命令:egrep "\$[0-9]+(\.[0-9][0-9])?" dollar.txt,这个没命令没有效果,可见,转移字符$好像没有用了。结果如下:
这里写图片描述
看到上面的运行结果是不对的,不过既然前面说到字符组可以让其中的元字符失效。所以这里我改成[$][0-9]+(\.[0-9][0-9])?,因为字符组使用要匹配一个字符,而[$]中只有一个美元符,所以美元符一定要匹配到。
命令:egrep "[$][0-9]+(\.[0-9][0-9])?" dollar.txt
这里写图片描述


还可以在上面的正则表示式中加入行结束符$来匹配只有美元的行


命令:egrep "[$][0-9]+(\.[0-9][0-9])?$" dollar.txt,匹配效果如下
这里写图片描述


解决$1,000无法匹配的问题,可以使用正则表达式:[$][0-9]{1,3}(,[0-9]{3})*$表示。


dollar.txt:

$123.12
$11.00
$11
$1000000
$1,000
$11,000
$100,000.34
$1,000,000.23

命令:egrep "[$][0-9]{1,3}(,[0-9]{3})*$" dollar.txt,效果如下。
这里写图片描述

然后加入小数点支持:[$][0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$,命令:egrep "[$][0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$" dollar.txt
这里写图片描述
从运行结果上来看,还差$1000000这一行没有匹配到。使用多选结构和上面匹配没有逗号分隔的正则合并一行就行了。合并后的正则表达式为:
[$]([0-9]{1,3}(,[0-9]{3})*|[0-9]*)(.[0-9]{1,2})?$,命令:egrep "[$]([0-9]{1,3}(,[0-9]{3})*|[0-9]*)(.[0-9]{1,2})?$" dollar.txt
可以惊喜的发现,上面的正则表达式还支持$.23这种形式的美元金额类型。
在上面的dollar.txt中末尾加入一行$.23,匹配效果如下。
这里写图片描述
可以看到,所有类型的美元符都能匹配到了。
这里再来解释一下[$]([0-9]{1,3}(,[0-9]{3})*|[0-9]*)(.[0-9]{1,2})?$这个正则表达式,

  • [$]表示美元符号
  • ([0-9]{1,3}(,[0-9]{3})*|[0-9]*)这是有两个子表达式构成的多选一结果
    • 多选结构的子表达式[0-9]{1,3}(,[0-9]{3})*支持$11,000,000形式的金额表示法,这个子表达式又分为两部分。
      • 第一部分[0-9]{1,3}中,{1,3}前面的字符组[0-9]重复1到3次。也就是表示一个1位数,或者2位数,或者3位数的数字。
      • 第二部分(,[0-9]{3})*表示可以有一个逗号,然后是一个3位数的数字,{3}表示前面的字符组[0-9]重复3次,所以第二部分(,[0-9]{3})*表示可以有多次逗号,然后是3位数,或者没有
    • 多选结果的第二个子表达式:[0-9]*支持$100000000这种没有逗号的美元金额表示方法。
  • (.[0-9]{1,2})?支持小数操作,可以有也可以没有小数,小数的位数可以是1到2位。
  • 最后的$,表示美元金额之后就是行结尾。

匹配HTTP/HTML URL

Web URL的形式可能有多种,所以构造一个能够匹配所有形式的URL的正则表达式颇有难度。不过稍微降低一点要求的话,我们可以用一个相当简单的正则表达式来匹配大多数常见的URL
常见的HTTP/HTML URL是下面这样的
http://hostname/path.html
当然以.htm结尾的也很常见。
hostname(主机名,例如www.yahoo.com)的规范比较复杂,但是我们知道,
跟在http://后面的就可能是主机名,所以这正则表达式就很简单,[-a-z0-9_.]+,path部分变化更多,所以我们需要使用[-a-z0-9_:@&?=+,.!/~*$]*注意,连字符必须放在字符组的开头,保证它是一个普通的字符,而不是用来表示范围的
综合起来,我们第一次尝试的正则表达式是:
egrep -i "\<http://[-a-z0-9_.]+/[-a-z0-9_:@&?=+,.!/~*$]*.html?\>" filename

匹配时间

匹配12小时制时间

匹配表示时刻的文字可能有不同的严格程度
[0-9]?[0-9]:[0-9][0-9]\s*(am|pm)能够匹配9:17 am或者12:30 pm但也能匹配无意义的时刻,如99:99pm
匹配12小时制的小时数
首先来看小时,我们知道如果小时是两位数的话,十位数只能是1,但是1?[0-9]显然能匹配19也能匹配0这样无意义的小时。所以更好的办法应该是把小时分为两种情况来处理,两位数(大于等于10)的时候是一种情况,个位数的时候是一种情况(10点以下)。所以用1[0-2]来匹配10点,1112点的情况。用[1-9]来表示1,2,3,4,5,6,7,8,9点这种情况。结合起来,小时就是(1[0-2]|[1-9]),
匹配分钟
分钟就比价简单十位数应该是[0-5],个位数应该是[0-9]

所以12小时制综合起来就是(1[0-2]|[1-9]):[0-5][0-9]\s*(am|pm)


实例:
time.txt:

1:30 am
10:20 am
1:40 pm
9:12 am
12:30 pm
18:89 pm #错误的表示方法
99:99 pm #错误的时间表示法
01:20 am #都是两位的时间表示法
00:30
23:59
24:00 #错误的时间
88:88 #错误的时间

使用正则表达式:(1[0-2]|[1-9]):[0-5][0-9]\s*(am|pm)进行匹配,命令:
egrep "\<(1[0-2]|[0-9]):[0-5][0-9]\s*(am|pm)\>" time.txt,匹配结果如下。
这里写图片描述
可以看到对于这个正则表达式可以成功匹配1:30 am这类的事件表示方法。但是无法匹配01:30 am这种表示方法。解决办法是把1-9点的正则表示式[1-9]改成0?[1-9]即可,新的正则表达式为:(1[0-2]|0?[1-9]):[0-5][0-9]\s*(am|pm),命令:egrep "\<(1[0-2]|0?[1-9]):[0-5][0-9]\s*(am|pm)\>" time.txt,匹配结果如下。
这里写图片描述
可以看到像01:20 am这种都是两位数表示的事件也能匹配到了。

匹配24小时制的时间

还是先按照上面分而治之的方法,
首先来匹配小时
小时数小于10,也就是0-9的时候,可以用[0-9]来表示,但是不确定是不是只用一位数如8,可以能用两位数表示,例如08,所以小于10的小时数用0?[0-9]来表示。而大于10,小于20的时间用1[0-9]来表示,例如10,19。大于20的小时,用2[0-3]来表示,所以,24小时制的小时用(2[0-3]|1[0-9]|0?[0-9])表示小时,
再来看分钟,分钟还是和上面一样简单,用[0-5][0-9]即可表示。
所以24小时制的事件表示:\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>,匹配命令:egrep "\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>" time.txt ,匹配效果如下。
这里写图片描述
可以看到像1:30 am这样的行居然也会匹配到,这是因为单词分界符在1:30结尾处就结束了。所以1:30看起来也像24小时制的,所以也会匹配出来。解决方法是一直匹配到行结尾,行结尾之前不允许出现am或者pm这样才是24小时制的。
新的正则表示式为\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>[^apm]*$,命令:egrep "\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>[^apm]*$" time.txt
这里写图片描述
这样就匹配出来的都是24小时制的时间了。


同时匹配12小时制和24小时制的时间

我们把上面匹配12小时制的正则表达式:\<(1[0-2]|0?[1-9]):[0-5][0-9]\s*(am|pm)\>和匹配24小时制的正则表达式\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>[^apm]*$使用多选结构合并一下,这样就能同时支持两个时间了。如下所示。

(\<(1[0-2]|0?[1-9]):[0-5][0-9]\s*(am|pm)\>|\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>[^apm]*$)

命令:egrep "(\<(1[0-2]|0?[1-9]):[0-5][0-9]\s*(am|pm)\>|\<(2[0-3]|1[0-9]|0?[0-9]):[0-5][0-9]\>[^apm]*$)" time.txt,匹配效果如下。
这里写图片描述
可以看到12小时制的时间和24小时制的时间都很好的匹配到了。不过这个表达式还是有问题的,后面不能出现任何的字母a,字母p和字母m,显然这就不是很正确。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值