正则表达式必知必会 - 匹配一组字符

2023-10-14 09:50:56 浏览数 (2)

一、匹配多个字符中的某一个

代码语言:javascript复制
mysql> set @s:='sales1.xls
    '> orders3.xls
    '> sales2.xls
    '> sales3.xls
    '> apac1.xls
    '> europe2.xls
    '> na1.xls
    '> na2.xls
    '> sa1.xls
    '> ca1.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='[ns]a.\.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| na1.xls,na2.xls,sa1.xls    | 68,76,84                            |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        这里使用的正则表达式以 [ns] 开头,这个集合将匹配字符 n 或 s。[ 和 ] 不匹配任何字符,它们只负责定义一个字符集合。接下来,正则表达式里的普通字符 a 匹配字符 a ,. 匹配一个任意字符,\. 匹配 . 字符本身,普通字符 xls 匹配字符串 xls。从结果上看,这个模式只匹配了 3 个文件名,与预期一致。

        注意,虽然结果正确,但模式 [ns]a..xls 并非完全正确。如果文件清单里还有一个名为usa1.xls的文件,它也会被匹配出来(开头的u会被忽略,匹配剩余的sal.xls)。这里涉及了位置匹配问题,将在以后专题讨论。

        正如看到的那样,对正则表达式进行测试是很有技巧的。验证某个模式能不能获得预期的匹配结果并不困难,但如何验证它不会匹配到不想要的东西可就没那么简单了。 字符集合在不需要区分字母大小写(或者是只需匹配某个特定部分)的搜索操作里比较常见。比如说:

代码语言:javascript复制
mysql> set @s:='The phrase "regular expression" is often
    '> abbreviated as RegEx or regex or REGEX.';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='[Rr]eg[Ee]x';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_extract(@s, @r, 'c'), regexp_extract_index(@s, @r, 0, 'c');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| RegEx,regex                | 57,66                               |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        这里使用的模式包含两个字符集合:[Rr] 负责匹配字母 R 和 r,[Ee] 负责匹配字母 E 和 e。这个模式可以匹配 RegEx 和 regex,但不匹配 REGEX。如果打算进行一次不需要区分字母大小写的匹配,不使用这个技巧也能达到目的。这种模式最适合用在从全局看需要区分字母大小写,但在某个局部不需要区分字母大小写的搜索操作里。

二、利用字符集合区间

代码语言:javascript复制
mysql> set @s:='sales1.xls
    '> orders3.xls
    '> sales2.xls
    '> sales3.xls
    '> apac1.xls
    '> europe2.xls
    '> sam.xls
    '> na1.xls
    '> na2.xls
    '> sa1.xls
    '> ca1.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='[ns]a[0123456789]\.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| na1.xls,na2.xls,sa1.xls    | 76,84,92                            |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        在这个例子里,模式的匹配对象是:第一个字符必须是 n 或 s ,第二个字符必须是 a,第三个字符可以是任何一个数字。文件名 sam.xls 没有出现在匹配结果里。在使用正则表达式的时候,会频繁地用到一些字符区间,如 0~9、A~Z 等。为了简化字符区间的定义,正则表达式提供了一个特殊的元字符:可以用连字符 - 来定义字符区间。

代码语言:javascript复制
mysql> set @r:='[ns]a[0-9]\.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| na1.xls,na2.xls,sa1.xls    | 76,84,92                            |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        模式 [0-9] 的功能与 [0123456789] 完全等价,所以这次的匹配结果与刚才那个例子一模一样。字符区间并不仅限于数字,以下这些都是合法的字符区间。

  • A-Z:匹配从 A 到 Z 的所有大写字母。
  • a-z:匹配从 a 到 z 的所有小写字母。
  • A-F,匹配从 A 到 F 的所有大写字母。
  • A-z,匹配从 ASCII 字符 A 到 ASCII 字符 z 的所有字母。这个模式一般不常用,因为它还包含 [ 和 ^ 等在 ASCII 字符表里排列在 Z 和 a 之间的字符。字符区间的首、尾字符可以是 ASCII 字符表里的任意字符。但在实际工作中,最常用的字符区间还是数字字符区间和字母字符区间。

        在定义一个字符区间的时候,一定要避免让这个区间的尾字符小于它的首字符,如 [3-1]。这种区间是没有意义的,而且往往会让整个模式失效。连字符 - 是一个特殊的元字符,它只有出现在 [ 和 ] 之间的时候才是元字符。在字符集合以外的地方,- 只是一个普通字符,只能与 - 本身相匹配。因此,在正则表达式里,- 字符不需要被转义。

        在同一个字符集合里可以给出多个字符区间。比如下面这个模式可以匹配任何一个字母(无论大小写)或数字,但除此以外的其他字符都不匹配: [A-Za-z0-9]

        下面的例子要查找的是 RGB 值(用一个十六进制数字给出的红、绿、蓝三基色的组合值,计算机可以根据 RGB 值把有关的文字或图象显示为由这三种颜色按给定比例调和出来的色彩)。在网页里,RGB 值是以 #000000(黑色)、#ffffff(白色)、#ff0000(红色)的形式给出的。RGB 值用大写或小写字母给出均可,所以 #FF00ff(品红色)也是合法的 RGB 值。

代码语言:javascript复制
mysql> set @s:='body {
    '>     background-color: #fefbd8;
    '> }
    '> h1 {
    '>     background-color: #0000ff;
    '> }
    '> div {
    '>     background-color: #d0f4e6;
    '> }
    '> span {
    '>     background-color: #f08970;
    '> }';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 --------------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '')      | regexp_extract_index(@s, @r, 0, '') |
 --------------------------------- ------------------------------------- 
| #fefbd8,#0000ff,#d0f4e6,#f08970 | 30,68,107,147                       |
 --------------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        这里使用的模式以普通字符 # 开头,随后是 6 个同样的 [0-9A-Fa-f] 字符集合。这将匹配一个由字符 # 开头,然后是 6 个数字或字母 A 到 F (大小写均可)的字符串。

三、排除

        字符集合通常用来指定一组必须匹配其中之一的字符。但在某些场合,我们需要反过来做,即指定一组不需要匹配的字符。换句话说,就是排除字符集合里指定的那些字符。不用逐个列出要匹配的字符,如果只是要把一小部分字符排除在外的话,这种写法就太冗长了。可以使用元字符 ^ 来排除某个字符集合。下面来看一个例子。

代码语言:javascript复制
mysql> set @s:='sales1.xls
    '> orders3.xls
    '> sales2.xls
    '> sales3.xls
    '> apac1.xls
    '> europe2.xls
    '> sam.xls
    '> na1.xls
    '> na2.xls
    '> sa1.xls
    '> ca1.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='[ns]a[^0-9]\.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| sam.xls                    | 68                                  |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        这个例子里使用的模式 [^0-9] 匹配的是任何不是数字的字符,也就是说,[ns]a[^0-9].xls将匹配sam.xls,但不匹配 na1.xls、na2.xls 或 sa1.xls。注意 ^ 的效果将作用于给定字符集合里的所有字符或字符区间,而不是仅限于紧跟在 ^ 字符后面的那一个字符或字符区间。

0 人点赞