正则表达式必知必会 - 匹配单个字符

2023-10-14 09:50:43 浏览数 (1)

一、匹配普通文本(plain text)

代码语言:javascript复制
mysql> set @s:='Hello, my name is Ben. Please visit
    '> my website at http://www.forta.com/.';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='Ben';
Query OK, 0 rows affected (0.00 sec)

mysql> select @s where regexp_like(@s, @r);
 -------------------------------------------------------------------------- 
| @s                                                                       |
 -------------------------------------------------------------------------- 
| Hello, my name is Ben. Please visit
my website at http://www.forta.com/. |
 -------------------------------------------------------------------------- 
1 row in set (0.00 sec)

        这里使用的是普通文本正则表达式,它将匹配原始文本里的 Ben。

代码语言:javascript复制
mysql> set @r:='my';
Query OK, 0 rows affected (0.00 sec)

mysql> select regexp_instr(@s, @r, 1, 1) m1, regexp_instr(@s, @r, 1, 2) m2, regexp_instr(@s, @r, 1, 3) m3;
 ------ ------ ------ 
| m1   | m2   | m3   |
 ------ ------ ------ 
|    8 |   37 |    0 |
 ------ ------ ------ 
1 row in set (0.00 sec)

        my 也是静态文本,它在原始文本里找到了两个匹配结果,出现位置分别是第 8 个字符开始和第 37 个字符开始。

1. 匹配多个结果

        绝大多数正则表达式引擎的默认行为是只返回第一个匹配结果。具体到上面那个例子,原始文本里的第一个 my 通常是一个匹配结果。怎样才能把两个或更多个匹配结果都找出来呢?绝大多数正则表达式的实现都提供了一种能够获得所有匹配结果的机制(通常以数组或是其他的特殊格式形式返回)。比如说,在JavaScript里,可选的g(global,全局)标志将返回一个包含所有匹配结果的数组。但是 MySQL 原生没有提供这种功能,需要自己实现。自定义函数参见“MySQL 正则表达式 - regexp_count、regexp_extract”。

代码语言:javascript复制
mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| my,my                      | 8,37                                |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

2. 字母的大小写问题

        正则表达式是区分字母大小写的,所以 Ben 不匹配 ben。不过,绝大多数正则表达式的实现也支持不区分字母大小写的匹配操作。比如说,JavaScript用户可以用i标志来强制执行不区分字母大小写的搜索。MySQL 用 match_type 参数指定是否区分大小写,缺省为不区分。

代码语言:javascript复制
mysql> set @r:='ben';
Query OK, 0 rows affected (0.00 sec)

mysql> select @s where regexp_like(@s, @r, 'i');
 -------------------------------------------------------------------------- 
| @s                                                                       |
 -------------------------------------------------------------------------- 
| Hello, my name is Ben. Please visit
my website at http://www.forta.com/. |
 -------------------------------------------------------------------------- 
1 row in set (0.00 sec)

mysql> select @s where regexp_like(@s, @r, 'c');
Empty set (0.00 sec)

二、匹配任意字符

        在正则表达式里,特殊字符或字符集合用来标示要搜索的东西。英文句号 . 字符可以匹配任意单个字符。

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

mysql> set @r:='sales.';
Query OK, 0 rows affected (0.00 sec)

mysql> select @s where regexp_like(@s, @r);
 -------------------------------------------------------------------------------------------- 
| @s                                                                                         |
 -------------------------------------------------------------------------------------------- 
| sales1.xls
orders3.xls
sales2.xls
sales3.xls
apac1.xls
europe2.xls
na1.xls
na2.xls
sa1.xls |
 -------------------------------------------------------------------------------------------- 
1 row in set (0.00 sec)

        正则表达式 sales. 可以找出所有以字符串 sales 起始,后跟另外一个字符的文件名。9 个文件里有3 个与该模式(pattern)匹配。

代码语言:javascript复制
mysql> select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');
 ---------------------------- ------------------------------------- 
| regexp_extract(@s, @r, '') | regexp_extract_index(@s, @r, 0, '') |
 ---------------------------- ------------------------------------- 
| sales1,sales2,sales3       | 1,24,35                             |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        注意,正则表达式使用字符串内容来匹配模式。匹配到的未必总是整个字符串,也可能是与某个模式相匹配的子串。在上面的例子里,使用的正则表达式并不能匹配完整的文件名,而是只匹配了其中一部分。如果需要把某个正则表达式的匹配结果传递到其他代码或应用程序里做进一步处理,就必须记住这种差异。. 字符可以匹配任意单个字符、字母、数字甚至是 . 字符本身。

代码语言:javascript复制
mysql> set @s:='sales.xls
    '> sales1.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='sales.';
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, '') |
 ---------------------------- ------------------------------------- 
| sales.,sales1              | 1,11                                |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        sales. 中的 . 匹配任意单个字符。

        在同一个正则表达式里允许使用多个 . 字符,它们既可以共同出现(一个接着一个——..将匹配连续的任意两个字符),也可以分别出现在模式的不同位置。再来看一个例子:

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

mysql> set @r:='.a.';
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, '') |
 ----------------------------- ------------------------------------- 
| sal,sal,sal,pac,na1,na2,sa1 | 1,24,35,47,68,76,84                 |
 ----------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        正则表达式 .a. 找到了 7 个匹配结果,因为只要有任意 3 个字符且中间那个字符是 a,该模式就能够匹配。

代码语言:javascript复制
set @r:='.a..';
select regexp_extract(@s, @r, ''), regexp_extract_index(@s, @r, 0, '');

        .a.. 中新增加的 . 将匹配任何一个多出来的字符(不管它是什么)。

三、匹配特殊字符

        .字符在正则表达式里有着特殊的含义。如果模式里需要一个 .,就要想办法来告诉正则表达式需要的是 . 字符本身而不是它在正则表达式里的特殊含义。为此必须在 . 的前面加上一个(反斜杠)字符来对它进行转义。 是一个元字符(metacharacter,表示“这个字符有特殊含义,代表的不是字符本身”)。因此,. 表示匹配任意单个字符,. 表示匹配 . 字符本身。在 MySQL 中,转义正则表达式的特殊字符需要使用两个反斜杠 。

代码语言:javascript复制
mysql> set @s:='sales.xls
    '> sales1.xls';
Query OK, 0 rows affected (0.00 sec)

mysql> set @r:='sales.';
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, '') |
 ---------------------------- ------------------------------------- 
| sales.,sales1              | 1,11                                |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

mysql> set @r:='sales\.';
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, '') |
 ---------------------------- ------------------------------------- 
| sales.                     | 1                                   |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        sales. 中的点用一个 做了转义,MySQL识别 . 为普通文本字符,而正则表达式引擎仍然将 . 匹配单个字符,结果与匹配模式 sales. 相同;sales. 用两个反斜杠转义,第一个 告诉 MySQL 后面的一个 是普通字符,而第二个 告诉正则表达式引擎其后的 . 为普通文本字符,所以结果只匹配了 sales.xls。

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

mysql> set @r:='.a.\.';
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.,na2.,sa1.             | 68,76,84                            |
 ---------------------------- ------------------------------------- 
1 row in set (0.00 sec)

        第一个 . 匹配 n 或 s,第二个 . 匹配 1、2 或 l。接下来,. 匹配了分隔文件名与扩展名的字符 . 本身。这个例子可以进一步改进:在模式中加入xls,避免匹配到像 sa3.doc 这样的文件名,就像下面这样:

代码语言:javascript复制
set @r:='.a.\.xls';

        在正则表达式里, 字符总是出现在具有特殊含义字符序列的开头,这个序列可以由一个或多个字符构成。如果需要搜索 本身,就必须对 字符进行转义。相应的转义序列是两个连续的反斜杠字符 。

代码语言:javascript复制
mysql> set @s:='sa1\.xls';
Query OK, 0 rows affected (0.00 sec)

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

mysql> select @s,@r,regexp_like(@s,@r);
 ---------- ------- -------------------- 
| @s       | @r    | regexp_like(@s,@r) |
 ---------- ------- -------------------- 
| sa1.xls | .a.. |                  0 |
 ---------- ------- -------------------- 
1 row in set (0.00 sec)

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

mysql> select @s,@r,regexp_like(@s,@r);
 ---------- --------- -------------------- 
| @s       | @r      | regexp_like(@s,@r) |
 ---------- --------- -------------------- 
| sa1.xls | .a.\. |                  1 |
 ---------- --------- -------------------- 
1 row in set (0.00 sec)

        . 可以匹配所有字符?未必。在绝大多数的正则表达式实现里,默认情况下 . 不能匹配换行符。

0 人点赞