前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习正则表达式 - 量词

学习正则表达式 - 量词

作者头像
用户1148526
发布2023-10-14 09:48:24
1380
发布2023-10-14 09:48:24
举报
文章被收录于专栏:Hadoop数据仓库Hadoop数据仓库

        {m,n} 是通用形式的量词,正则表达式还有三个常用量词,分别是 +、?、*。它们的形态虽然不同于 {m,n},功能却是相同的,因此也可以把它们理解为“量词简记法”。具体说明见下表。

常用量词

{m,n}等价形式

说明

*

{0,}

出现零次、一次或多次

+

{1,}

出现至少一次

?

{0,1}

出现至多一次

一、贪心、懒惰和占有

        量词自身是贪心的。贪心量词会首先匹配整个字符串。尝试匹配时,它会选定尽可能多的内容,也就是整个输入。量词首次尝试匹配整个字符串,如果失败则回退一个字符后再次尝试。这个过程叫做回溯(backtracking)。它会每次回退一个字符,直到找到匹配的内容或者没有字符可尝试为止。此外,它还记录所有的行为,因此相较另两种方式它对资源的消耗最大。

        懒惰(有时也说勉强)量词则使用另一种策略。它从目标的起始位置开始尝试寻找匹配,每次检查字符串的一个字符,寻找它要匹配的内容。最后,它会尝试匹配整个字符串。要使一个量词成为懒惰的,必须在普通量词后添加一个问号 ?。

        占有量词会覆盖整个目标然后尝试寻找匹配内容,但它只尝试一次,不会回溯。占有量词就是在普通量词之后添加一个加号 +。

二、用 *、+ 和 ? 进行匹配

        这些量词默认是贪心的,这意味它们在第一次尝试时会尽可能多地匹配字符。

代码语言:javascript
复制
drop table if exists t1;
create table t1 (a varchar(10));
insert into t1 values
('1'),('22'),('333'),('4444'),('55555'),('666666'),
('7777777'),('88888888'),('999999999'),('0000000000');

        .* 匹配任何字符零次或多次,因此会以贪心的方式匹配所有行。

代码语言:javascript
复制
mysql> select regexp_substr(a,'.*') from t1;
+-----------------------+
| regexp_substr(a,'.*') |
+-----------------------+
| 1                     |
| 22                    |
| 333                   |
| 4444                  |
| 55555                 |
| 666666                |
| 7777777               |
| 88888888              |
| 999999999             |
| 0000000000            |
+-----------------------+
10 rows in set (0.01 sec)

        9* 匹配 0个到多个9,因此会以贪心的方式匹配所有行。数字9的行完全匹配,其它行匹配空串。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'9*') from t1;
+-----------------------+
| regexp_substr(a,'9*') |
+-----------------------+
|                       |
| 999999999             |
+-----------------------+
2 rows in set (0.01 sec)

        9.* 匹配 9 后面跟着任何字符零次或多次,因此包含数字9的行,其它行不匹配,因此返回NULL,注意不是空串!

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'9.*') from t1;
+------------------------+
| regexp_substr(a,'9.*') |
+------------------------+
| NULL                   |
| 999999999              |
+------------------------+
2 rows in set (0.00 sec)

        9+ 匹配 1个到多个9,结果包含数字9的行。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'9+') from t1;
+-----------------------+
| regexp_substr(a,'9+') |
+-----------------------+
| NULL                  |
| 999999999             |
+-----------------------+
2 rows in set (0.00 sec)

        9? 匹配 0个或1个9。数字9的行只匹配第一个字符9,其它行匹配空串。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'9?') from t1;
+-----------------------+
| regexp_substr(a,'9?') |
+-----------------------+
|                       |
| 9                     |
+-----------------------+
2 rows in set (0.00 sec)

        99? 匹配 9后面跟0个或1个9。数字9的行匹配前两个字符9,其它行不匹配。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'99?') from t1;
+------------------------+
| regexp_substr(a,'99?') |
+------------------------+
| NULL                   |
| 99                     |
+------------------------+
2 rows in set (0.00 sec)

三、匹配特定次数

        使用花括号可以限制某个模式在某个范围内匹配的次数,未经修饰的量词就是贪心量词。例如 7{1} 会匹配第一次出现的7。

代码语言:javascript
复制
mysql> select regexp_substr(a,'7{1}') from t1 where a regexp '7{1}';
+-------------------------+
| regexp_substr(a,'7{1}') |
+-------------------------+
| 7                       |
+-------------------------+
1 row in set (0.01 sec)

        要匹配一个或多个数字7,只要加一个逗号即可。

代码语言:javascript
复制
mysql> select regexp_substr(a,'7{1,}') from t1 where a regexp '7{1,}';
+--------------------------+
| regexp_substr(a,'7{1,}') |
+--------------------------+
| 7777777                  |
+--------------------------+
1 row in set (0.00 sec)

        7+ 和 7{1,} 本质上是一样的;7* 和 7{0,}、7? 和 7{0,1} 也是一样的。还可以匹配 m到n 次,比如 7{3,5} 会匹配三个、四个以及五个7。

代码语言:javascript
复制
mysql> select regexp_substr(a,'7{3,5}') from t1 where a regexp '7{3,5}';
+---------------------------+
| regexp_substr(a,'7{3,5}') |
+---------------------------+
| 77777                     |
+---------------------------+
1 row in set (0.00 sec)

        可以看出,花括号(或者说范围语法)是最灵活和精确的量词。

四、懒惰量词

        理解懒惰特性最好的方式就是看看实际应用,尝试用问号 ? 匹配零个或者一个5。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'5?') from t1 where a regexp '5?';
+-----------------------+
| regexp_substr(a,'5?') |
+-----------------------+
|                       |
| 5                     |
+-----------------------+
2 rows in set (0.01 sec)

        请再加一个 ? 来使量词变为懒惰的。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'5??') from t1 where a regexp '5??';
+------------------------+
| regexp_substr(a,'5??') |
+------------------------+
|                        |
+------------------------+
1 row in set (0.00 sec)

        现在它看起来只匹配空串,其原因是该模式已经是懒惰的了。也就是说,它不会强制匹配第一个5。懒惰的基本特性就是匹配尽可能少的字符。试一下匹配零次或多次的量词。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'5*?') from t1 where a regexp '5*?';
+------------------------+
| regexp_substr(a,'5*?') |
+------------------------+
|                        |
+------------------------+
1 row in set (0.01 sec)

        它也只匹配空串,因为它可以选择匹配最少的次数——零次。再试一下匹配一次或多次。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'5+?') from t1 where a regexp '5+?';
+------------------------+
| regexp_substr(a,'5+?') |
+------------------------+
| 5                      |
+------------------------+
1 row in set (0.00 sec)

        懒惰特性使其只匹配了一个5。它只需要做到这个程度就可以了。使用 m和n 方式匹配时就更为有趣了。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'5{2,5}?') from t1 where a regexp '5{2,5}?';
+----------------------------+
| regexp_substr(a,'5{2,5}?') |
+----------------------------+
| 55                         |
+----------------------------+
1 row in set (0.00 sec)

        只匹配了两个5,而不像贪心量词那样匹配五个。

        下表列出了懒惰量词。什么时候懒惰式匹配最实用?如果想匹配最少而不是最多数目的字符,就可以使用懒惰量词。

语法

说明

??

懒惰匹配零次或一次

+?

懒惰匹配一次或多次

*?

懒惰匹配零次或多次

{n}?

懒惰匹配n次

{n,}?

懒惰匹配n次或多次

{m,n}?

懒惰匹配m至n次

五、占有量词

        占有式匹配很像贪心式匹配,它会选定尽可能多的内容。但与贪心式匹配不同的是它不进行回溯。它不会放弃所找到的内容,这也是把它称为占有式(possessive)的原因。占有量词的优点是速度快,因为无需回溯。当然,匹配失败的话也很快。

        为了理解这一点,我们先尝试匹配以零开头的多个零。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'0.*+') from t1 where a regexp '0.*+';
+-------------------------+
| regexp_substr(a,'0.*+') |
+-------------------------+
| 0000000000              |
+-------------------------+
1 row in set (0.00 sec)

        匹配了所有的零。占有式的匹配看起来和贪心式的匹配是一样的,但没有回溯。可以证明一下,输入带有结尾零的表达式。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'.*+0') from t1 where a regexp '.*+0';
Empty set (0.01 sec)

        没有匹配——原因就是没有回溯。它一下就选定了所有的输入,不再回过来查看。它一下子没在结尾找到零,也不知道该从哪里找起。如果将加号去掉,它会找到所有的0,因为它变回贪心式匹配了。

代码语言:javascript
复制
mysql> select distinct regexp_substr(a,'.*0') from t1 where a regexp '.*0';
+------------------------+
| regexp_substr(a,'.*0') |
+------------------------+
| 0000000000             |
+------------------------+
1 row in set (0.00 sec)

        当知道文本中的内容就知道在哪里可以找到匹配时,应该会使用占有量词。它不在乎是否会选定所有内容。占有式匹配有助于提高匹配的性能。下表列出了占有量词。

语法

说明

?+

占有匹配零次或一次

++

占有匹配一次或多次

*+

占有匹配零次或多次

{n}+

占有匹配n次

{n,}+

占有匹配n次或多次

{m,n}+

占有匹配m至n次

六、示例——括号字符串计数

        有一张表 t1 存储用户评论内容,如下所示(只列出相关列):

        现在想得出每种评论字数的个数,每个字符包括标点、空格、表情符号都算一个字,但每对中括号连同其中的内容只算一个字。对于上面的数据行,结果为:

        第一感觉这是使用正则表达式的场景。只要将每对中括号连同其中的内容替换为单个字符,再用char_length函数求长度即可。查询语句如下:

代码语言:javascript
复制
select char_length(regexp_replace(Content,'\\[.*?\\]', 'A')) r,count(*) 
  from t1 group by char_length(regexp_replace(Content,'\\[.*?\\]', 'A')) 
 order by r;

        \\[ 和 \\] 用于将中括号转义为普通字符。非 dotall 模式下的正则表达式中,“.”表示匹配除换行符 \n 之外的任何单字符,“*”表示零次或多次。所以 “.*” 连在一起就表示任意字符出现零次或多次。没有“?”表示贪婪模式。比如 a.*b,它将会匹配最长的以 a 开始,以 b 结束的字符串。如果用它来搜索 aabab 的话,它会匹配整个字符串 aabab。又比如模式src=`.*`, 它将会匹配以 src=` 开始,以`结束的最长的字符串。用它来搜索 <img src=``test.jpg` width=`60px` height=`80px`/> 时,将会返回 src=``test.jpg` width=`60px` height=`80px`。

        “?”跟在“*”后边用时,表示懒惰模式,就是匹配尽可能少的字符。这就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。a.*?b 匹配最短的以 a 开始,以 b 结束的字符串。如果把它应用于 aabab 的话,它会匹配 aab(第一到第三个字符)和 ab(第四到第五个字符)。又比如模式 src=`.*?`,它将会匹配 src=` 开始,以 ` 结束的尽可能短的字符串,且开始和结束中间可以没有字符,因为 * 表示零到多个。用它来搜索 <img src=``test.jpg` width=`60px` height=`80px`/> 时,将会返回 src=``。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、贪心、懒惰和占有
  • 二、用 *、+ 和 ? 进行匹配
  • 三、匹配特定次数
  • 四、懒惰量词
  • 五、占有量词
  • 六、示例——括号字符串计数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档