写在最前面
我自己个人几乎是没有想到过会有写博客的这一天的,但是应老师的要求以及之前看过很多的大佬的博客上都说,写博客是自己的一个思路的整理和输出的过程,我也按耐不住写博客的兴趣了,开始总结一些在学习过程中学到的东西。在这篇博客里面先讲哈工大软件构造实验给我最深印象的正则表达式。
正则表达式是什么
正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
你说了一大堆,所以正则表达式到底是干什么的?别急啊,下一节就告诉你正则表达式到底是干什么的。
正则表达式能干什么
举个很简单的栗子:假如你的女朋友是一位住房销售顾问,你看着她每天没日没夜地爬网站,找电话,累得半死,而你又心疼,那你怎么办?(当然是帮助她啦 )
假定你已经学会了爬虫技术,如何将网页上的内容爬取下来,爬下来之后你看到的就是一堆完完全全不是人看的东西(其实是我不懂网页 ),那么难道你要自己去看网页源代码然后找出来有可能是数字的东西吗?我们程序员当然选择说不,我们要自动化处理!现在就该请出我们本文的主角正则表达式
r
e
g
e
x
regex
regex先生了。
上面的定义说过了,正则表达式是通过某些模式匹配某些特定的文本,那么我们最重要的目的就是要想清楚,我们到底想要正则表达式找什么样的文本,并且用正则表达式能够看得懂的语言告诉它它的任务是什么。现在我们的任务是要找电话,电话的格式是什么,或者说遵循什么样的规律呢?我们很容易想到,我们的电话都是十一位数的数字,所以出现连续的十一位数的字符串可能就是电话。那么我该怎么告诉我的程序要给我找到连续的十一位数呢?正则表达式其实也和其他的编程语言一样,它的主要表示方法也是由一些符号组成的。例如上述的例子,用正则表达式写的话结果就是这样子的“[0-9]{11}”
,这是什么东西呢?下面我来给大家讲解一下。
首先看前面的[0-9]
,大家可能已经都猜出来这是什么意思了。没错,这个代表的就是所有的数字的意思,所有的数字不就是
0123456789
0123456789
0123456789吗?而这个杠杠在我们日常生活中代表的就是从什么东西到什么东西的意思,也就是
0
0
0到
9
9
9。后面的{11}
想必在前面的[0-9]
的谜底揭晓之后,大家也都心里有数了,这个代表的当然就是前面的数字应该连续出现
11
11
11个嘛!
有好事者可能要问了,那我要是想让这个数字出现一个范围呢?比如数字有连续的
15
15
15-
18
18
18位都是可以接受的呢?这个时候我们就要引入一个新的表达式了那就是{m,n}
。前面的大括号里只有一个数字而现在的这个大括号里却有两个,代表的是出现的次数不能小于
m
m
m次但也不大于
n
n
n次。为了防止模糊,强调一下
m
m
m和
n
n
n都是可以匹配的哦。所以假设如果你要匹配某长度在
15
15
15到
18
18
18的号码的话,那你可以这样写
"[0-9]{15,18}"
。
但其实我们上面讨论的那个正则表达式的匹配精度是不够的,为什么呢?分为以下几种原因:
- 手机号码开头的数字一定是1,你的程序如果返回给你 22222222222 22222222222 22222222222这样的号码你会开心吗?
- 手机号码不一定是 11 11 11位连在一起,例如 133 − 3333 − 3333 133-3333-3333 133−3333−3333, 1333 333 3333 1333\space333\space3333 1333 333 3333等各种奇葩的分隔符
- 电话号码是包含了区号的,这样就会造成很多的不便,例如 ( 027 ) 33333333 (027) 33333333 (027)33333333, 027 − 33333333 027-33333333 027−33333333, ( 027 ) − 33333333 (027)-33333333 (027)−33333333,甚至 ( 027 ) − 3333 − 3333 (027)-3333-3333 (027)−3333−3333这样的魔鬼。
对上述问题的改进
- 我们可以告诉正则表达式限制开头的数字一定是
1
1
1,这样正则表达式就变成了这样
"[1]{1}[0-9]{10}"
回忆一下刚才讲过的,[]
中的内容代表一个匹配单元,{}
代表的是出现多少次。既然我们想让 1 1 1在开始仅仅出现一次,那么就可以写成[1]{1}
,后面的[0-9]{10}
已经在前面讲过了这里就不再赘述。在这个问题里还想多讲两句,如果想要匹配多于一次不设上限怎么写呢?假如要匹配数字,没有位数上限的要求,那我们应该这么写[0-9]{1,}
。这里的逗号后面没有内容说明的是它只需要大于等于一次就好了,至于具体有多少次我不管,反正都匹配下来就是了,或者还可以这么写更简洁[0-9]+
这个和[0-9]{1,}
是完全等价的写法。这里有一张表,可以说明一些最常用的元字符的功能。
元字符 | 功能 |
---|---|
* | 匹配前面的模式任意多次 |
+ | 匹配前面的模式至少一次 |
? | 匹配前面的模式 0 0 0或 1 1 1次 |
^ | 匹配行首字符 |
$ | 匹配行尾字符 |
\ | 转义字符 |
. | 匹配除了\r和\n之外的任何单个字符 |
- 通过上面讲的元字符的用法,大家应该已经可以自己写出这个问题的结果来了。我们改进上一个问题的正则表达式应该是
[1]{1}[0-9]{10}
,现在我们要求在第三位数字或者第四位数字后插入一个分隔符-
,再在第七位数字后插入一个分隔符-
,那么我们很快就能写出其中的一个结果来[1]{1}[0-9]{2}[-]{1}[0-9]{4}[-]{1}[0-9]{4}
。大家观察一下这个正则表达式,是不是有重复的单元?[-]{1}[0-9]{4}[-]{1}[0-9]{4}
这两个似乎是重复的呢,我们可以精简一下吗?当然可以,我们从直接上来说应该怎么写呢?答案是[1]{1}[0-9]{2}([-]{1}[0-9]{4}){2}
,用小括号来表明作用的范围,这里表明[-]{1}[0-9]{4}
这个模式应该出现两次。 - 这里应该就不需要我细讲了吧,大家应该都可以自己改进了,我直接贴答案出来吧。
( 1 ) . (1). (1). ( 027 ) 33333333 (027) 33333333 (027)33333333这种类似的正则表达式写法为[(]{1}[0-9]{3,4}[)]{1}[0-9]{7,8}
。因为有些地区的区号是三位数,例如北京 010 010 010,武汉 027 027 027,有些地方的区号是四位数,例如哈尔滨 0451 0451 0451,杭州 0571 0571 0571,各城市的电话号码长度也不尽相同,有七位数也有八位数,所以大家可以自己根据需要修改。
( 2 ) . (2). (2). 027 − 33333333 027-33333333 027−33333333这种类似的正则表达式写法为[0-9]{3,4}[-][0-9]{7,8}
( 3 ) . (3). (3). ( 027 ) − 33333333 (027)-33333333 (027)−33333333这种类似的正则表达式写法为[(]{1}[0-9]{3,4}[)-]{1}[0-9]{7,8}
( 4 ) . (4). (4). ( 027 ) − 3333 − 3333 (027)-3333-3333 (027)−3333−3333这种类似的正则表达式写法为[(]{1}[0-9]{3,4}[)]{1}([-]{1}[0-9]{4}){2}
好啦到这里这篇博客就算入门啦,大家也可以用正则表达式来处理自己需要处理的文本啦,这里贴一个百度百科的网站,若想继续学习正则表达式的语法规则大家可以参照这个网站上的内容。下一期博客我想谈一谈如何在Java中使用正则表达式。正则表达式-百度百科
该文章被直接复制到本人的github pages中,网址为Java中正则表达式的用法