正则表达式

基础正则

元字符:

  • .:匹配任意单个字符,但不能匹配换行符\n
  • *:匹配前面那个字符0或多次
  • ?:匹配前面那个字符0或一次
  • +:匹配前面那个字符1次以上
  • {M,N}:匹配前面那个字符至少M,最多N次
  • {M,}:匹配前面那个字符至少M次,最多无限制
  • {,N}:匹配前面那个字符最多N次(最少当然是0次)。注意,perl正则不支持这种方式
  • {M}:匹配前面那个字符正好M次
  • 锚定:锚定的意思是匹配位置,而非匹配字符实体
    • ^:匹配行首位置,注意匹配的是位置,不是字符
    • $:匹配行尾位置,注意匹配的是位置,不是字符

特殊且常用的的组合正则表达式:

  • ^$:它表示匹配空行
  • .*:匹配任意长度的任意字符,但不能匹配换行符。真正的匹配任意长度的任意字符,见下面

需要解释清楚的是这些量词(也就是上面匹配的次数元字符)的特殊性:当使用了匹配多次的量词时(如匹配3-5次的{3,5}),且量词前面的字符有多种可能性(如中括号序列[abc]),那么量词的次数可以作用于任一字符。有些不好理解,但看示例就知道了:

[abc]{3,5}     # 表示abc任意字符都可以出现,比如全是a,或者ab同时出现,但总的出现次数为3-5次
.*          # 表示任一字符(除换行符),可以任意出现任意次数,它不表示a之后就必须全是a
.+          # 表示任一字符(除换行符),可以任意出现至少一次,它不表示a之后就必须全是a

另外,.无法匹配换行符。可能你不太理解为什么需要匹配换行符,它主要用在:

  1. 多行模式。例如sed的多行模式下,要跨行匹配需要手动指定"\n",如/^a.*\nb.*/
  2. 明确指定了行分隔符为非"\n"的情况。例如awk可以使用RS变量指定输入行分隔符

中括号

中括号表示的是匹配任意一个,一般它和字符集的排序规则有关,不同工具采取的排序规则可能也不一样。

  • [abcd...]:匹配中括号内的任意一个字符
  • [^abcd...]:拒绝匹配中括号内的任意字符
  • [a-z]:匹配字母a到z
  • [A-Z]:匹配字母A到Z
  • [0-9]:匹配0-9,也就是匹配数字

关于字母的排序:

  • perl中,A-Z排在a的前面,所以[A-z]表示所有大小写字母
  • grep中,A-Z排在z的后面,所以[a-Z]表示所有大小写字母
  • 还有些工具中,大小写的排序规则是aAbBcC…zZ,所以[a-C]表示aAbBcC共6个字母

字符类

是专门命名的中括号序列;除了字符类,还有等价类、排序类,但基本用不上,只用字符类。

  • [:alpha:]:匹配字母,等价于[a-zA-Z]
  • [:digit:]:匹配数字,等价于[0-9]
  • [:xdigit:]:匹配十六进制数,等价于[0-9a-fA-F]
  • [:upper:]:匹配大写字母,等价于[A-Z]
  • [:lower:]:匹配小写字母,等价于[a-z]
  • [:alnum:]:匹配数字或字母,等价于[0-9a-zA-Z]
  • [:blank:]:匹配空白,包括空格和制表符
  • [:space:]:匹配空格,包括空格、制表符、换行符、回车符等各种类型的空白
  • [:punct:]:匹配标点符号。包括:! ' " # $ % & ( ) * + , . - _ / : ; < = > ? @ [ \ ] ^ { | } ~`
  • [:graph:]:绘图类。包括:大小写字母、数字和标点符号。等价于[:alnum:]+[:punct:]
  • [:print:]:打印字符类。包括:大小写字母、数字、标点符号和空格。等价于[:alnum:]+[:punct:]+space
  • [:cntrl:]:控制字符类。在ASCII中,这些字符的八进制代码从000到037,还包括177(DEL)

需要注意的是,通常字符类在真正使用过程中,会再加上一个中括号,例如[[:alpha:]]。之所以如此,是因为这些字符类只是一种命名好的字符集合。例如[:lower:]对应的字符集合是a-z,而不是[a-z],所以要想让其表示这些命名字符类中的任一字符,需要再加上一层括号[[:lower:]],它才等价于[a-z]。可能会更有助于理解使用字符类的时候为什么要加两个中括号的例子是[^[:lower:]],它表示不包含任何小写字母。

反斜线序列

不同的工具,同一工具不同的版本,支持的反斜线序列能力不同。以下列出了部分常见序列。

以下所说的单词,一般来说只包含数字、字母和下划线,即[_0-9a-zA-Z]

以下几种反斜线序列,基本上所有工具都支持:

  • \b:匹配单词边界处的空字符
  • \B:匹配非单词边界处的空字符
  • \<:匹配单词开头处的空字符
  • \>:匹配单词结尾处的空字符
  • \w:匹配单词构成部分,等价于[_[:alnum:]]
  • \W:匹配非单词构成部分,等价于[^_[:alnum:]]

以下几种,有些工具不支持,但perl都支持:

  • \s:匹配空白字符,等价于[[:space:]]
  • \S:匹配非空白字符,等价于[^[:space:]]
  • \d:匹配数字,等价于[0-9]
  • \D:匹配非数字,等价于[^0-9]

由于元字符.默认无法匹配换行符,所以需要匹配换行符的时候,可以使用特殊组合[\d\D]来替换.,换句话说,如果想匹配任意长度的任意字符,可以换成[\d\D]*,当然,前提是必须支持\d\D两个反斜线序列。

分组捕获和反向引用

基础正则中,使用括号可以对匹配内容进行分组并暂时保存,分组后会有分组编号,可以使用反斜线加编号\N的方式反向引用这些分组。

分组编号的方式是从左向右计算括号数,无论如何嵌套,第一个左括号对应的分组一定是编号1,用\1来引用,第二个左括号对应的分组一定是编号2,用\2来引用,依此类推。

例如grep的分组捕获:匹配两个连续相同的字母。

echo "abcddefg" | grep -E "(.)\1"

可以认为分组就是变量赋值的过程。例如,上面示例的匹配过程如下:
1.匹配第一个字母a,放进分组,即赋值给变量(假设变量名为$1),即$1="a",再继续执行正则表达式匹配过程的反向引用,它引用的是$1,于是表示第一个字母a后面还要是字母a,于是匹配失败。
2.匹配第二个字母b,放进分组,即$1="b",再匹配后一个字母,于是匹配失败。
3.字母c同样如此。
4.匹配字母d,放进分组,即$1="d",再匹配后一个字母,发现匹配成功,于是$1被保存下来。
5.已经匹配成功,于是结束。

对于只使用基础正则的工具来说,一般都只能引用\1\9共9个反向引用,最多自己额外提供一个所有表示匹配内容的反向引用(例如sed提供的&)。对于超出10个的分组,使用基础正则的工具一般来说是无能为力的。

再者,基础正则仅仅只是将分组匹配到的内容捕获,在正则操作结束后就丢失。但对于一门完整编程语言来说,这远远不够,几乎所有编程语言(如perl/java/python等)都会将正则的分组匹配内容保存为变量,使得可以在正则结束之后再次引用甚至修改它们。例如上面例子中分组捕获的字母d,如果换成perl,即使在这个匹配过程结束后,还是可以去引用这段分组。

二选一

  • pattern1 | pattern2:匹配竖线左边,或者匹配竖线右边都算匹配成功

关于二选一的结构,几点需要说明:

  1. 因为竖线元字符的优先级很低,所以ab|cd匹配的是"ab"或"cd",而不是abd或acd。
  2. 成功匹配了左边,就不会再去对右边进行匹配。
  3. 反向引用失败问题:竖线将两边的分组隔开,右边的永远无法反向引用左边的分组

在二选一结构种,两个反向引用问题的典型例子:

例如a(.)|b\1将无法匹配"ba",因为评估了左边就不会评估右边。

例如([ac])e\1|b([xyz])\2t的左边能匹配aea或cec,但不能匹配cea或aec,右边能匹配bxxt或byyt或bzzt。但如果将\2换成\1,即([ac])e\1|b([xyz])\1t,将无法匹配b[xyz]at或b[xyz]ct,因为第一个分组括号在左边,无法参与右边的正则评估。

位置匹配(锚定)

字符匹配会消耗字符
位置匹配,只是匹配字符,不消耗字符。
只匹配位置,不匹配字符,所以不会小号字符数量,也称为及零宽断言

  • ^匹配行首
  • $匹配行尾
  • <:匹配单词开头处的位置
  • >:匹配单词结尾处的位置
  • \b:匹配单词边界处的位置(开头和结尾),所以\bword\b等价于\<word\>
  • \B:匹配非单词边界处的位置

正则表达式的匹配过程


每一轮正则的匹配,都需要从正则的第一个元素从头开始匹配。

第一轮匹配:

  • 扫描第一个字符,和正则表达式的第一个元素进行匹配
  • 如果匹配失败,则意味着这一轮的正则匹配失败

第二轮匹配

  • 扫描下一个字符,从头开始和正则表达式进行匹配
  • 如果匹配成功,则继续扫描下一个字符和正则表达式的下一个元素进行匹配
    • 如果这个字符匹配失败,则导致这一轮正则匹配失败
    • 交还除第二轮匹配开始的首字符外的所有字符
    • 交还之后,从交还的第一个字符开始进入下一轮匹配

第三轮匹配

  • 如果这一轮正则匹配成功,则不交还匹配成功的字符
  • 然后从匹配成功的下一个字符进入下一轮匹配

某轮正则匹配成功匹配成功,则消耗所有匹配成功的字符
某轮匹配失败,则消耗本轮匹配的首字符,剩余字符被交还

量词(重复匹配次数)

1.在基础正则表达式中,对于量词的元字符需要架上反斜线进行转义

关于量词需要注意结论:量词它是正则表达式中的一个隐含的修饰符,它修饰它前面一个字符或前面一个子表达式,它自身不是正则表达式中的独立元素,量词和它所修饰的字符或子表达式组合起来才是正则表达式中的独立元素
量词可能会出现大量的回溯,而回溯所是正则表达式中的性能杀手

  • {m} 表示匹配前一个字符或前一个子表达式m次
    • “a{3}”:匹配a3次。 aaa aab aaaa
    • “[abc]{3}”:aaa bbb ccc aba abc cba
  • {m,n} (m<n)表示匹配前一个字符或前一个表达式最少m次,最多n次
    • “a{3,5}” aaa aaaa aaaaa
  • {m,}表示匹配前一个字符或前一个子表达式至少m次
    • “3{2,}” 33 333 333333 333333333333333
  • {,n}表示匹配前一个字符或前一个子表达式至多n次(注:不一定会支持该语法)
    • 注意点:匹配0次也成功,只不过这时候匹配的是空字符,grep没法显示出来
  • ? 表示匹配前一个字符或前一个子表达式0或1次,等价于{0,1},等价于{,1}
  • * 表示前一个字符或前一个子表达式匹配0次或多次,即任意次数,等价于{0,}
    • a3*b:可以匹配ab、a3b、a33b、a333333333333b
    • .* :匹配任意长度的任意字符
      • 问:为什么.* 匹配的是任意长度。而不是某个字符任意长度?
  • +表示匹配前一个字符或前一个子表达式1或多次,即至少一次,等价于{1,}

这些量词均为贪婪匹配模式,即尽可能多的去匹配符合条件的字符。例如正则表达式ab.*c去匹配字符串abbcdecfc,其中.*部分匹配的将是bcdecf。

基础正则和扩展正则都支持贪婪匹配,不支持非贪婪匹配。如果想要在基础正则或扩展正则上实现非贪婪匹配,比较复杂。但也能实现。例如:

#字符串: ”abc:def:ghi:jkl“

#匹配目标,去第一列,即”abc“
#正则表达式:
  	#错误写法:".*:"
  	#正确写法:"[^:]*"
#匹配目标:取前两列,即"abc:def"
#正则表达式:"[^:]*:[^:]*"

怎么实现:已中括号取反的方式排除分隔符,然后用量词去修饰

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oXTiZXKe-1635054356261)(D:/Typora/image-20210901103410207.png)]

二选一表达式

基础正则表达式中,需要对|进行转义,使用|来表示二选一表达式。
竖线 | 分割左右两个正则子表达式,表示匹配任一一个即可。例如a|b表示a或者b,在结果上等价于[ab];[0-5]|\sa 表示0、1、2、3、4、5、”a“

使用二选一子表达式注意几点:
1、二选一元字符优先级很低,所以abc|def表示的是abc或def,等价于(abc)|(def),而不是ab(c|d)ef
2、二选一结构和中括号表达式的性能比较:

  • 对于DFA引擎来说,二选一结构和中括号的性能是完全一样的,a|b|c|d|e[abcde]完全等价
  • 对于NFA引擎来说(per1),二选一结构性能远低于中括号性能,a|b|c|d|e意味着5倍的回溯数量,而[abcde]它只有单词回溯数量
    • 能在中括号表达式就不要二选一结构
    • 把尽可能匹配到的选择写在二选一结构的前面

案例

[root@localhost ~]# echo   "404  not found"  |  grep  'not|not found'
404  not found
[root@localhost ~]# echo   "404  not found"  |  perl -nE  '/not|not found/;say $&'
not

3、二选一结构在分组捕获时,只有成功匹配时才会能反向引用

  • x(abc)|(def)y中,要么\1可用,要么\2可用,不会同时有用
  • ([ab])x|cdy\1无法匹配cdya或cdyb

分组捕获

使用小括号包围一部分正则表达式(pattern),这部分正则表达式即成为一个分组整体,也即成为一个子表达式。
小括号有两个隐含的功能

  • 1.分组
  • 2.自动捕获:保存分组的匹配结果,以后可以去引用这个捕获结果

根据左括号的位置决定第几个分组。
例如:(abc)def([a-d]){3}(([0-9]abc(def){2})(hgi)

分组后可以使用\N来反向引用对应的分组的匹配结果,N是1-9的正整数,\1表示第一个分组表达式的匹配结果,\2表示第二个分组表达式的匹配结果:
例如:

echo  "you  see  see  you" |  grep  -E  '(.*)\1'
echo  "you  see  see  you" |  grep  -E '((.*)(.*)) \3 \2'
[root@localhost ~]# grep  '([0-9]+):\1'  /etc/passwd
[root@localhost ~]# grep  '(bin):/\1'   /etc/passwd

注意:反向引用所引用的是分组匹配后的结果,不是分组表达式
正则表达式(abc|def) and \1xyz可以匹配字符串"abc and abcxyz" 或 def and defxyz,但是不能匹配"abc and defxyz"或 ”def and abcxyz“。

如果想要引用分组表达式而不是负责捕获的结果,需要递归正则。 Perl

echo  "abc  and  defxyz" |  grep  -P  '(abc|def) and  \1xyz'
echo  "abc  and  defxyz" |  grep  -P  '(abc|def) and   (?1)xyz'
abc  and  defxyz

匹配模式修饰符

i修饰符:忽略大小写的匹配 “abcABC” “/ab /i”
g修饰符:全局匹配

或 ”def and abcxyz“。

如果想要引用分组表达式而不是负责捕获的结果,需要递归正则。 Perl

echo  "abc  and  defxyz" |  grep  -P  '(abc|def) and  \1xyz'
echo  "abc  and  defxyz" |  grep  -P  '(abc|def) and   (?1)xyz'
abc  and  defxyz

匹配模式修饰符

i修饰符:忽略大小写的匹配 “abcABC” “/ab /i”
g修饰符:全局匹配

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值