5句话,让你的正则表达式水平突飞猛进!(第3-5句)

龙逸凡

创作者俱乐部成员

上一篇文章我们说了:

在正则表达式眼中,字符串由位置和字符组成。正则表达式就是去匹配字符或位置。

文章链接:

对于正则的初学者来说,匹配位置通常比匹配字符更难理解一些。所以,这五句话都是围绕位置来讲解的。

接上篇,我们继续

第3句

位置相当于空字符

上一篇文章我们介绍了字符串是由字符和位置组成,

比如字符串“Excel“每个字符前后都有一个位置。

我们通常说“位置”,一般认为它是有顺序的,但正则表达式中的“位置”并不能用第几个位置来指称。我们可以将正则表达式中的位置理解成空字符

在Excel中文本需要用双引号括起来,双引号中间没有内容,就是空字符。用""表示空字符。也就是说,在正则表达式眼中“Excel”相当于:

""+E+""+x+""+c+""+e+""+l+""

来测试一下是否如此

证明1:

用REGEXP的替换模式,将位置替换为@字符看效果

=REGEXP(B1,"",2,"@")

插入字符的更多写法见文后的图片。

证明2:

提取字符串中的所有字符(含位置)

公式:

=REGEXP(B1,".*?")

正则表达式解释:

正则表达式.*?是一个非贪婪(懒惰)匹配的模式,它的含义是:

.:匹配任意单个字符(除了换行符)。

*:匹配前面的字符零次或多次,即匹配任意数量的字符。

?:问号“?”放在*星号和+加号等量词后是一个非贪婪修饰符,表示尽可能少地匹配字符。

要点:

  • 非贪婪的 .*? 会尝试匹配最少的字符,所以在第一次应用时,它会首先去匹配零个字符(即不匹配任何字符),零个字符匹配不上,再匹配1个字符……。当匹配零个字符时,就会将位置(空字符)匹配上。

龙逸凡:这堆文字看起来比较绕、比较上头,理解后会大大提升你的正则……

偷懒读者:打住!说关键的,有什么用?

龙逸凡:我们来看一个实战案例,一个你肯定用得上的案例。用这方法可以在每个字符间插入一个你喜欢的符号,比如:长得象雪花的星号*。

公式:

=REGEXP(B3,"",2,"*")

偷懒读者:然后呢?拎瓶雪花勇闯天涯?

龙逸凡:不,这些雪花能帮你找到意中人去风花雪月年年暮暮朝朝。将上面的公式放到VLOOKUP函数的第一参数,实现你梦寐以求的查找功能:根据不连续简称查全称

偷懒读者:???

龙逸凡:看图说话

偷懒读者:哇……!太棒了,的确梦寐以求!这公式是我一直寻找的。众里寻它千百度,蓦然回首,公式却在,偷懒公号处。只是,我看不懂这公式,能否解释一下?

龙逸凡:星号在正则表达式中是量词,表示0个及多个,但在Excel函数中,它是通配符,表示任意多个任意字符(但不代表星号本身)。函数REGEXP(B3,"",2,"*")的计算结果是“*偷*懒*1*”。用它做Vlookup函数的第一参数,意思是在去查找符合这样规则的字符串:

在“偷”、“懒”,“1”三个字符中间有若干个其他字符的文本。

在E列的名称列表中,符合这种格式的文本就是“《“偷懒”的技术1:打造财务Excel达人》”。所以VLOOKUP将它做为查找结果。

偷懒读者:明白了,很巧妙。

龙逸凡:在理解“位置就是空字符”后,大家应该就能理解正则表达式专业术语“零宽断言”中的“零宽”了。位置相当于空字符,它是没有宽度(字符数)的,所以说是零宽

偷懒读者:我好象理解“位置就是空字符”了。

龙逸凡:不,你没有。

偷懒读者:为什么?

龙逸凡:来做个小测试,公式=REGEXP("Excel","^^^",2,"@"),将字符串开始的位置(连续三个)替换为@符号,其计算结果是什么?备选答案:

A、会显示出错;B、@@@Excel;C、@Excel

偷懒读者:字符的开始处只有一个,用三个^去匹配肯定匹配不上,不够用啊。所以肯定是A,当然,不排除是B。

龙逸凡:我们看完第五句话再来做这道题。先看第四句。

第四句

正则表达式还可用字符来确定位置,比如“前面是XX(在XX后面)”、“前面不是XX(不在XX后面)”、“后面是XX(在XX前面)”、“后面不是XX(不在XX前面)”。

用此方法时,请屏蔽掉那些装逼拗口晦涩难懂的专业术语“零宽断言、正向预查、反向预查、顺序肯定环视、逆序否定环视、零宽度正预测先行断言、零宽度负回顾后发断言”。

正则表达式写法及其作用见下表:

助记方法:

  • 通常用于表示是某种模式,比如上一篇文章用到的(?m)多行模式、还有(?i)忽略大小写的模式。

  • = 是;

  • ! 表示排除、否定,不是;

  • < 指向左边,也就是前面

合起来看

?<=”左边是,也就是“前面是,在XX后面”,

?<!”左边不是。

龙逸凡:是不是很好理解?

偷懒读者:理解了,不会再用错了

案例:

来看个小案例深入理解一下。比如字符串"Excel"中X和C之间的那个位置,我们可以描述为“在C前面”或“在x后面”

老办法,还是用正则函数的替换模式将“位置”替换为@符号,让位置可视化,更直观。

需求:

在字母"c"之前插入一个@号:

公式:

=REGEXP(B1,"(?=c)",2,"@")

在字母"x"之后插入一个@号

公式:

=REGEXP(B1,"(?<=x)",2,"@")

字符串"Excel"的其他位置,可以描述为不在c之前,不在x后

在“不在c前面”的位置插入一个@号

公式:

=REGEXP(B1,"(?!c)",2,"@")

在“不在x后面”的位置插入一个@号

公式:

=REGEXP(B1,"(?<!x)",2,"@")

来看几个实战案例吧

案例1:

提取“本”字前面的册数、提取“元”字前面的金额(含小数点),将其转为数值然后求和。

龙逸凡:Regexp函数是文本函数,运算结果是文本,前面放两个负号,负负得正,四则运算一下,即可将文本数字转为数值,以方便求和。

偷懒读者:这个知识点我知道。我不明白的是字符串中的“234本”,只有4后面有“本”字,2和3后面并没有“本”字,它们为什么能提取出来。

龙逸凡:“\d+(?=本)”的意思是:前面是若干个连续数字,后面跟了个“本”字,也可理解为:“本”字前面的若干个连续数字。

偷懒读者:哦……终于明白了。

案例2:

提取中文逗号和#号之间的内容

案例3:

判断是否姓“龙”

=REGEXP(B3,"^(?!龙)",1)

^ 是开始的位置

(?!龙) 后面不是“龙”字

^(?!龙) 合起来就是“开始位置后不是“龙”,也就是不姓“龙”。

案例4:

提取主办、主管人员的姓名(不带职位)

公式:

=REGEXP(B1,"[一-龟]+(?=主办|主管)")

附:如果要提取主办、主管人员的姓名(带职位)

公式:

=REGEXP(B1,"[一-龟]+(主办|主管)")

案例5:

提取B列字符串中的数量

公式:

=REGEXP(B2,"(?<=\*)\d+")

=REGEXP(B2,"\d+(?=[桶缸罐坛]|$)")

案例6:

给银行账号每4位添加一空格

看起来好象没问题,如果换成逗号就会发现有问题

从前往后添加逗号的公式:

=REGEXP(B1,"(\d{4})",2,"\1,")

从后往前添加逗号的

=REGEXP(B1,"(?=(\d{4})+$)",2,",")

那怎么解决呢?

需要用到第五句话的知识。

第五句

字符只能被匹配一次,位置可以被多次匹配。

比如下面的示例:

公式:

=REGEXP(B1,".e",2,"@")

B1单元格内容是“eel”,用正则表达式".e"去匹配,首先用"."去匹配,第一个"e"能匹配上,被消耗掉(后面的正则表达式就不能再匹配此字符了)。然后用正则表达式中的"e"去匹配字符串中的第二个"e",也能匹配上。

但是,位置被匹配后,并不会被消耗掉,还能被其他的正则表达式匹配

比如公式:

=REGEXP(B1,"^(?=E)\A",2,"@")

首先用^去匹配,会被匹配到字符串的开头,这位置不会被消耗掉。

接着,用(?=E)去匹配,匹配到的还是"E"前面的位置,也就是字符串的开头。

接着,又去用\A去匹配,这是字符串的绝对开头,匹配的还是字符串的开始处。

也就是说字符串的“开始”这个位置被匹配了三次。

现在再来做=REGEXP("Excel","^^^",2,"@")这道题,你就知道其计算结果是“@Excel”了。

龙逸凡:现在你终于知道为什么找不到喜欢的人了吧?

偷懒读者:为啥?

龙逸凡:就三个备选答案,你都能完美地错过正确答案,你还指望能从14亿中准确地找到你喜欢的人?

偷懒读者:

注意,这个地方就不能去套第三句话“位置即空字符”。否则就会得出下面的推论:各字符中间的空字符(位置)可以是一个,也可以是任意多个。比如:“Excel”相当于:

""+E+""+x+""+c+""+e+""+l+""

也相当于

""+""+……""+E+""+x+""+……+""+c+""+e+""+l+""

这就让人理解不了,完全没法理解。

一头雾水,一面懵逼!

难道这表示位置的空字符是薛定谔家的?

所以,记住:

字符只能被匹配一次,位置可以被多次匹配。

我们来看看这个知识点的实战应用案例

案例7:

在前面的案例6给银行账号从后往前每4位添加逗号,当账号长度是4的倍数时,会在最前面多出一个逗号。如下图

如何避免这种情况呢?

我们只需在原正则表达式的基础上,再添加一个位置判断:

当是行首时,就不添加逗号。

换另一种表达:

当不是行首时,才添加逗号

完整的条件是:

从后往前,每四位所在的位置,并且不是行首的位置处,加一个逗号。

公式:

=REGEXP(B33,"(?!^)(?=(\d{4})+$)",2,",")

扩展案例1:

给字符串中的数字添加千位分隔符,但不能给年份添加。

公式:

=REGEXP(B1,"(?<=\d)(?=(?:\d{3})+($|[^\d年]))",2,",")

扩展案例2

密码校验:由数字字母下划线组成,长度不少于八位。且至少一个大写字母、一个小写字母、一个数字

公式:

=REGEXP(B5,"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)\w{8,}$",1)

知识点:

  • 至少包含1个大写字母:

(?=.*[A-Z])

  • 至少包含1个小写英文字母

(?=.*[a-z])

  • 至少包含1个数字

(?=.*[0-9])

扩展案例3

判断字符串中是否不包含XXX

公式

=REGEXP(B5,"^((?!52|1234).)*$",1)

知识点:

  • 不包含XXX的正则表达式

^((?!字符串).)*$

利用位置可以被多次匹配的特性,还能解决“前面是 (?<=...)” 和“前面不是 (?<!...)”位置匹配模式下,不能使用不确定量词的问题。

案例7:

B列是一些订单数据,现在要求在其金额后添加“元”字,但不能在编号数字后添加“元”,已有“元”的也不再能添加。

分析:

由于数据不规范,“订单编码”也可能被写成“订单编号”,更麻烦的是,“编码”或“编号”字符后的冒号可能有,也可能没有。我们能想到的正则表达式是:

(?<!编[号码][::]?)

标红的问号在此处表示[::]是0个或1个。

【小总结:问号的作用有多种,①量词,0个或1个;②在量词后表示非贪婪匹配;③还可用于表示是某种匹配模式】

由于“前面是 “(?<=...) 和”前面不是“ (?<!...)【也就是反向环视】模式下不能使用不确定量词。所以,(?<!编[号码][::]?)这种写法是错误的,我们可以利用位置可以被多次匹配的特点,写成:

(?<!编[号码])(?<!编[号码][::])

完整的公式:

=REGEXP(B3,"(?<!编[号码])(?<!编[号码][::])\b([0-9.]+)(\r|\n|$)",2,"\1元\2")

第五句不太好理解,大家慢慢消化。

附上在字符间各种花式插入星号的正则表达式

Excel偷懒的技术微信公众号还有正则表达式的更多文章,请点击下面的合集阅读:

正则表达式

重庆
浏览 368
5
6
分享
6 +1
5
5 +1
全部评论 5
 
WPS 冲浪队长

社区管理员

halo,方便加个企微吗
· 广东省
回复
龙逸凡

创作者俱乐部成员

5月份加了你的。微信名:龙逸凡
· 重庆
回复
 
点赞收藏慢慢学
· 山东省
回复
 
收藏学习!
· 安徽省
回复
 
点赞收藏
· 广东省
回复