查看原文
其他

正则表达式的另一个功能:匹配模式!

IT服务圈儿 2022-09-11

The following article is from 程序员架构 Author 立体的萌

作者丨立体的萌

源丨程序员架构(ID:chengxuyuanjg)原创,转载请联系jb_quaner。

引子

上周我们讲过《站在正则表达式背后的……》,在这篇文章中我没细讲解了正则表达式的匹配原理,这一篇文章我来详细讲解一下匹配模式

匹配模式听起来高大上,其实就是匹配时使用的规则;设置好相应的模式,会影响到正则表达式的识别和字符的匹配规定;


第一种模式:不区分大小写模式

通常情况下,我们关注的都是文本的含义而不是文本的形式,比如the、THE、The在我们的眼里就没有什么区别,但是在区分大小写的计算机眼里就是有区别的, 你如果写以上三种中的任意一种都会匹配出全部的三种来,这就要求你在匹配的时候把单词的大小写给写清楚,如果单词都像the这么短就好了,可是理想很丰满,现实很骨感,长单词才是主流,单词一长匹配起来也就特别麻烦,为了解决这样的问题,正则表达式提供了不区分大小写模式;

不区分大小写模式有两种指定方式:

1.通过模式修饰符来指定;de

2.通过预定义的常量作为特殊参数传入来指定;

通过模式修饰符来指定

正则表达式有点像是深度学习——受神经科学的启发,正则表达式源于人类神经系统的早期研究,有两位科学家研究出了一套使用数学方式来描述神经网络的方式,并在1956年发表论文的时候明确提出了正则表达式的概念,“正则表达式是正则集的代数的表达式,”,真是太高大上了,让我这个生物盲情何以堪,反正明白正则表达式是生物学家受人类神经系统的启发而创造出来的然后被广泛应用于计算机的工具就好;

所谓模式修饰符就是:模式对应的单个字符,在这里的字符就是?,

# 一个用Python来写的例子
re.search(r"(?i)the","the")!=None   #=>True;
re.search(r"(?i)the","THE")!=None   #=>True;
re.search(r"(?i)the","The")!=None   #=>True;

这种方式比较常用,主要是各个语言的通用性;

通过预定义的常量作为特殊参数传入来指定;

在java中属于Pattern类;

Pattern.pattern=Pattern.compile("the",Pattern.CASE_INSENSITIVE);
pattern.matches("THE").fine();


第二种模式:单行模式

我们都知道,元字符中的点(.)有多通用,简直就是万金油,几乎所有的字符都可以匹配到,不过有个例外:/n,以HTML为例,如果写:

<script>\s.*?</script>

是不对的!因为.*?最多只能匹配到第一行的末尾;

这种情况下可以用[\s\S],所以这样写就对了:

<script>\s[\s\S]*?</script>

不过,我们都习惯了点,毕竟点太常见了,在Java中经常需要点来给出另一个类的属性和函数,为了适应大家的习惯,正则表达式还有单行模式;

单行模式对应的模式修饰符是s(Single line),具体的写法是:?s

(?s)<script>\s.*?</script>


第三种模式:多行模式

首先说明一点,多行模式跟单行模式一点关系都没有!

单行模式影响的是点号(.)的匹配规则

默认模式下,点号可以匹配到除了换行符之外的所有的字符,

单行模式下,点号可以匹配到包括换行符在内的所有的字符;

多行模式影响的是^和$的匹配规则

默认模式下,^和$匹配的是整个字符串的起始位置和结束位置,

多行模式下,^和$匹配的是字符串内部某一行的起始位置和结束位置;

多行模式的模式修饰符就是m(Multiline),具体的写法是?m

//指定
multilineString="1 line\nNot digit\n2 line"
lineBeginWithDigitRegex=r"(?m)^\d.*"
for line in re.findall(lineBeginWithDigitRegex,multilineString):
print line

//给行末添加句号
multilineString="1 line\nNot digit\n2 line"
lineEndRegex=r"(?m)$"
print re.sub(lineEndRegex,".",multilineString)

第四种模式:注释模式

只要是写代码就必须要有注释,正则表达式的注释和Python的一样;

dataRegex=r"""
(?x)    #enable multiline and extended mode


除此之外,还有什么?

以上四种匹配模式只是每种语言都会有的,通用的几种,也就是几种语言的正则表达式的匹配模式的交集,除了这几种,还有其他的模式,这些都是每种语言的独有的,比如java,还有Pattern.UNIX_LINES和Pattern.CANON_EQ;


模式与反向引用

所谓反向引用就是他引用之前的表达式匹配的文本;

如果之前的表达式使用了某种模式,那么引用的时候是否会继承此模式呢?

//单行模式与反向引用
"a\na\n".matches("((?s)a.)\\1");
"a\na\n".matches("(?s)(a.)\\1");
//多行模式与反向引用
"abc".matches("((?m)^a.)\\1");
"abc".matches("(?m)(^a.)\\1");


哪里都少不了冲突

这东西就跟死锁一样,两头倔驴拉着车往相反的方向使劲拉的情况又出现了,试想一下,如果用(?-i)取消了不区分大小写的匹配模式,又使用了预定义常量指定整个正则表达式采用不区分大小写的匹配模式,

Pattern.compile("(?-i)a",
      pattern.CASE_INSENSITIVE).matches("A").find();=>false

模式修饰符具有更高的优先级!如果预定义常量制指定了整个正则表达式采用某种模式,同时正则表达式的内部又使用了关闭该模式的失效修饰符,那么 失效修饰符之后的部分都不受预定义常量所指定的模式的影响;

Pattern regex=Pattern。compile("a(?-i)a",Pattern.CASE_INSENSITIVE);
regex.matches("aa").find();//=>True
regex.matches("Aa").find();//=>True
regex.matches("aA").find();//=?True    


关于作者

笔名立体的萌,是个工作还不到一年就辞职考研的的菜鸟,上学学习的是java,但是工作用的是C#,C#用途远远没有java范围广,再加上java是我的母语,所以很像回归java,不仅仅是提升技术能力,也有往大数据等高端领域发展的想法。

请作者吃糖


本文作者:立体的萌 

个人网址:https://blog.csdn.net/weixin_46107282

声明:本文为 程序员架构 原创投稿,未经允许请勿转载。

1、小女孩把快速幂奥秘探索出来了!

2、290家公司都在用的任务调度系统,还在Github上开源了

3、图片验证码把我逼成了人工智障

4、RedMonk编程语言排行:Java和Python并列第二、Dart首次进入前20

点分享

点点赞

点在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存