01
引言
在程序员的日常工作中,似乎人人都会碰到正则表达式。诸如:校验密码强度、身份证号、邮箱格式等用法,它的功能如此强大,几乎可以完成任何搜索与匹配的功能。同时,它又如此繁琐与庞杂,让人望而却步,从未想过真正掌握与了解。需要使用时,常见做法是在搜索引擎苦苦求索。诚然,一些常用场景已有成熟的解决方案可供使用,然而,针对高度定制化需要自行构造正则的应用场景,无不抓耳挠腮痛苦万分。本文通过简单介绍正则表达式的基本语法及应用示例,希望在正则的使用上能带来一些帮助。
02
简介
正则表达式(regular expression,简称regex)起源于上世纪90年代在数学领域的一些研究工作,计算机领域在借鉴这些研究成果与思路之后,开发出了Unix平台中的Perl语言和grep等工具。在之后相当长的一段时间里,正则表达式仅用于Unix平台。而现在,几乎所有的平台都支持正则表达式。
那么,什么是正则表达式。简单来说,正则表达式是用来匹配和处理文本的字符串,有自己的特殊语法与指令。但是正则表达式并不是一种完备的程序语言,甚至算不上是一种能够直接安装并运行的程序。准确来说,正则表达式更像是内置于其他编程语言或软件产品中的“迷你”语言。
在如今常用的高级编程语言中,清一色都支持regex。只不过在具体的表达形式上略有差异。比如在java中,由于转义字符的原因,在遇到’\\’时需要使用’\\\\’进行转义,例如:常见的\\d需要写成\\\\d。在js中,则可以直接写成’\\d’。
03
基础规则
纯文本类
Regex: Everbright
Text: Everbright Technology CO.LTD
Everbright是一个正则表达式,可能很反直觉,但它确实是。正则表达式可以包含纯文本内容,甚至可以只是纯文本。它可以匹配到Everbright Technology CO.LTD中的Everbright。
再来看一个纯文本的正则表达式例子:
Ebchinatech
https://www.ebchinatech.com/ebchinatech/622504/index.html
正则表达式是大小写敏感的,因此上边例子中的Ebchinatech是匹配不到ebchinatech的。
特殊字符类
在正则表达式中,特殊字符有特定的含义与用法。
“
特殊字符.
”
“.”可以匹配除了换行符之外的任意一个字符
c.t
cat cot c1t c_t cert
“.”字符也可以一次使用多个,例如“..”可以连续匹配两个任意字符
c..t
cert count
“.”也可以出现在表达式的任意位置
.a.
nas IaaS Paas
如果需要匹配的文本中,本身带有“.”
例如:test.txt example.xls 这时就需要对.进行转义处理,在表达式中加上一个“\\”
例如:.a..\\.xls 就可匹配到.xls
IaaS.xls paas.xls aaaa.txt
“
特殊字符*
”
“*”在正则表达式中指的紧挨着“*”的子表达式的可出现的次数>=0
go* 意味着“*”之前的字母o出现次数>=0
g god good goooood
也可以把“.”与“*”组合起来使用
g.* 表示可匹配字母g后跟随任意字符(除换行符外)>=0次
g god g123d gb2312
“
特殊字符+
”
“+”在使用上与*有类似的地方,指的是紧挨着+的子表达式可出现的次数>=1
go+
g god good goooood
“
特殊字符?
”
“?”号表示子表达式可出现的次数为0或1次,也称作可选量词
colou?r 意味着?之前的u,可以出现也可以不出现,所以两个单词皆可匹配
color colour
“
特殊字符()
”
“()”可以简单理解成对表达式进行分组,通常和其他表达式组合使用
do(es)? 把括号中的es作为一个整体,通过与后边的“?”组合,表示es可有可无,故可以匹配如下结果。
do does
(ha)+ 同理,还有这个+号的例子
haha hahaha hah
“
特殊字符|
”
“|”可以理解成条件判断中的或,只选择其中的一个
(c|r)at 表示匹配以c或r字母开头的字符
fat cat rat
“
特殊字符[]
”
“[]”用来定义一个字符集,有多种使用方式和表达形式
① [ABC] 可以匹配[…]中的任意一个字符
a[ABC]c
aAc aBc aCc aABc
② [^ABC] ^表示匹配除了[…]中的任意字符,相当于对[…]取非
a[^ABC]c
abc aABCc aABc
③ [A-Z] 表示的是一个包含两个端点的范围,即匹配从A-Z的任何一个字符,相似的还有[0-9],[a-z]。
[A-Z]
Abc Bcd
“
特殊字符{}
”
“{}”用来表示重复的次数,有三种写法
① {x,y} 表示匹配{}前面子表达式x-y次
go{1,2}d 表示匹配o出现1到2次的字符
god good goood
② {x,} 表示匹配{}前面子表达式x到+次
fo{2,}d 因此,这个表达式可以匹配到fooood,但匹配不到fod
food fod fooood
③ {x} 这种写法明确了匹配的次数,即只匹配子表达式x次
fo{2}d
food fod
“
特殊字符^
”
“^”字符单独出现时,限定了匹配字符串的开始位置,通过下边的例子可以很好理解
^talk ([a-z]+ ?)* “^”表示限定了开始的单词只能是talk,()作为一个整体,()后边的*表示()里的内容出现的次数>=0次。()中使用了字符集[a-z]匹配英文字母,后边的+表示字母可以出现>=1次。后边跟上一个空格和一个?表示空格可以出现也可以不出现。所以,这段正则就可以匹配到如下的内容:
talk is cheap
it is well known that talk is cheap
如果去掉开头的“^”,则可以得到下边的结果
talk ([a-z]+ ?)*
talk is cheap
it is well known that talk is cheap
“
特殊字符$
”
“$”字符的含义与^正好相反,用来标识字符串的结尾。需要注意的是“$”出现在结尾字符的后边
([a-z]+ ?)*code$ 第二个字符串没有以code结尾,所以匹配不到
talk is cheap show me the code
talk is cheap
04
特定字符类型
对于一些特定的字符类别,例如:纯数字、空白字符等,为了方便使用会将这类常用的表达式,进行简写定义。
“
匹配数字
”
上边的介绍中已经谈到了匹配数字的方式,比如:
[0-9]可以匹配到0到9的任意一个数字,\\d就是对其的一种简写定义,等价于[0-9],可以匹配任何一个数字
[^0-9]匹配的是非数字的表达式,\\D与之等价。
可以看到\\d与\\D的含义正好相反。
接下来还会看到更多类似的定义,相同之处在于,通过大小写字母的区分,进行取反。
“
匹配数字与字母
”
\\w可以用来匹配数字加字母以及下划线,即\\w等价于[a-zA-Z0-9_]
相反\\W则表示任何一个非数字、字母或下划线字符,等价于[^a-zA-Z0-9_]
“
匹配空白字符
”
空白字符指的是换行、制表位等特殊的字符,如\\n \\t \\r等,
在正则表达式中,使用\\s来匹配所有的空白字符
\\s等价于[\\f\\n\\r\\t\\v]
同理\\S等价于[^\\f\\n\\r\\t\\v]
05
正则表达式实战
前边简单介绍了正则表达式的常见语法与用法,接下来用实际案例,一起感受下正则表达式在真实环境中的应用。需要注意的一点是,对于相同的文本内容,可能会有多个不同的正则表达式,区别在匹配的精确度不同。一般来说,越简洁的表达式可读性越好,越长的表达式精确度越高。
匹配ipv4地址
ipv4地址在日常工作中特别常见,我们的案例就以它展开。
ipv4地址的地址范围是从0.0.0.0-255.255.255.255,为了便于管理,还对其进行了不同的分类。本则案例中,不考虑地址的分类及实际使用情况,仅通过正则表达式将这些地址匹配区分出来。
通过搜索引擎查询,大概会得到这样一个正则:
\\d+\\.\\d+\\.\\d+\\.\\d+
通过分析可以发现,这个流传甚广的正则是错误的。
表达式\\d等价于[0-9],也就是0-9中的任意一个数字,\\d+表示数字的位数>=1直至无穷。显然这并不符合ipv4地址的范围。例如该表达式可以匹配上一个随机滚键盘出来的地址:
367345.543.123.457
介于此,我们尝试重写这段正则。
首先,只考虑组成ip的十进制数字,会发现它是一个0-255的自然数,位数最少为1位,最多为3位。
①当只有个位数字时,可以取到0-9任意一个数,因此:
可以得出表达式\\d
②当有两位数字时,十位不为0,个位可取到0-9中的任意一个数,因此在上述表达式的基础上可以得到:
[1-9]\\d
考虑将个位数和十位数的情况进行合并,加上?即可表示同时表示十位数与个位数
[1-9]?\\d
③当有三位数时,需要考虑多种情况。百位的数字只能取1-2,当百位数字为1时,十位、个位数字可取0-9中任意一个,无限制。当百位数字为2时,十位数字只能取0-5并且且当十位数字为5时,个位数字只能取0-5。
使用正则表达式翻译上述规则,则有:
百位数为1时:
1\\d{2}
百位数为2,十位数为5时:
25[0-5]
百位数为2,十位数不为5时:
2[0-4]\\d
上述各种情况,均可能出现,即逻辑关系‘或’,所以用|连接得到最终的正则表达式:
(25[0-5])|(2[0-4]\\d)|(1\\d{2})|([1-9]?\\d)
使用上述表达式可以正确匹配到组成ipv4地址的数字,一个ip地址由四组数字与三个分割符.组成。分隔符作为正则表达式中的关键字,需要进行转义。最终的正则表达式如下:
((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)
最后留下一个课后作业
尝试编写一条正则表达式,
能同时匹配下边的三条字符串
define service add name TCP46101-46104 protocol 6 ports \'46101-46104 \'
define service add name TCP8000-8008 protocol 6 ports \'8000-8008 \' comment \' \'
define service add name TCP443 protocol 6 ports \'443 \' comment \'https\'
参考资料:
《正则表达式必知必会》[美] Ben Forta 著 杨涛 等译
https://regexlearn.com/zh-cn/cheatsheet
https://www.runoob.com/regexp/regexp-syntax.html
文章作者:栗 石
排版设计:王蔚棋
手绘插画:岳 媛
在看不好意思,那就点个赞吧
原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/33066.html