第9章 用正则表达式进行搜索
正则表达式介绍
使用MySQL正则表达式
基本字符匹配
进行OR匹配
匹配几个字符之一
匹配范围
匹配特殊字符
匹配字符类
匹配多个实例
定位符
一、正则表达式介绍
正则表达式是用来匹配文本的特殊的串(字符集合)。可以实现更加复杂的过滤条件。
如果你想从一个文本文件中提取电话号码,如果你需要查找名字中间有数字的所有文件,如果你想在一个文本块中找到所有重复的单词,如果你想替换一个页面中的所有URL为这些URL的实际HTML链接,都可以使用正则表达式。
所有种类的程序设计语言、文本编辑器、操作系统等都支持正则表达式。
正则表达式用正则表达式语言来建立,正则表达式语言是用来完成刚讨论的所有工作以及更多工作的一种特殊语言。与任意语言一样,正则表达式具有你必须学习的特殊的语法和指令。
二、使用MySQL正则表达式
正则表达式的作用是匹配文本,将一个模式(正则表达式)与一个文本串进行比较。MySQL用where
子句对正则表达式提供了初步的支持,允许你指定正则表达式,过滤select
检索出的数据。
1、基本字符匹配
例如,检索prod_name
包含文本1000
的所有行。
select prod_name
from products
where prod_name regexp '1000' // like '%1000%'
order by prod_name;
除关键字like
被regexp
替代外,这条语句看上去非常像使用like
语句。它告诉MySQL,regexp
后跟的东西作为正则表达式(与文字正文1000
匹配的一个正则表达式)处理。
select prod_name
from products
where prod_name regexp '.000' // like '_000'
order by prod_name;
这里使用了正则表达式.1000
。.
是正则表达式语言中一个特殊的字符。它表示匹配任意一个字符。
注意:匹配不区分大小写为区分大小写,可使用binary
关键字,如where prod_name regexp binary 'JetPack .000'
。
2、进行OR匹配
为搜索两个串之一,使用|
。
select prod_name
from products
where prod_name regexp '1000|2000'
order by prod_name;
使用|
从功能上类似于select
语句中使用or
语句,多个or
条件可以并入单个正则表达式(1000 | 2000 | 3000
)。
3、匹配几个字符之一
匹配一组特定的单一字符,可通过指定一组[
和]
扩起来的字符来完成。
select prod_name
from products
where prod_name regexp '[123] Ton'
order by prod_name;
这里使用了正则表达式[123] Ton
。[123]
定义一组字符,它的意思是匹配1
或2
或3
,因此,1 ton
、2 ton
和3 ton
都可以匹配且返回。
[]
是另一种形式的or
语句。事实上,正则表达式[123] ton
为[1|2|3] ton
的缩写。
select prod_name
from products
where prod_name regexp '1|2|3 Ton' // 匹配'1'或'2'或'3 Ton'
order by prod_name;
注意,上面的写法匹配'1'或'2'或'3 Ton'。除非把字符|括在一个集合中,否则它将应用于整个串。
字符集合也可以被否定,即,它们将匹配除指定字符外的任何东西。为否定一个字符集,在集合的开始处放置一个^
即可。因此,尽管[123]
匹配字符1
、2
或3
,但[^123]
却匹配除这些字符外的任何东西。
4、匹配范围
集合可用来定义要匹配的一个或多个字符。 例如,下面的集合将匹配数字0到9
[0123456789]
为简化这种类型的集合,可使用-
来定义一个范围。
[0-9]
范围不限于完整的集合,[1-3]
和[6-9]
也是合法的范围。此外,范围不一定只是数值的,[a-z]
匹配任意字母字符。
select prod_name
from products
where prod_name regexp '[1-5] Ton'
order by prod_name;
5、匹配特殊字符
正则表达式语言由具有特定含义的特殊字符构成。我们已经看到.
、[]
、|
和-
等,还有其它一些字符。请问,如果你需要匹配这些字符,应该怎么办呢?例如,如果要找出包含.
字符的值,怎样搜索?
select vend_name
from vendors
where wend_name regexp '\\.'
order by vend_name;
为了匹配特殊字符,必须用\
为前导。这种处理就是所谓的转义。
\
也用来引用元字符(具有特殊含义的字符)。
元字符 | 说明 |
---|---|
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 制表 |
\v | 纵向制表 |
匹配:为了匹配反斜杠(\
)字符本身,需要使用\\
。\
或\
:多数正则表达式实现使用单个反斜杠转义特殊字,以便能使用这些字符本身。但MySQL要求两个反斜杠(MySQL自己解释一个,正则表达式库解释一个)。
6、匹配字符类
存在找出你自己经常使用的数字、所有字母字符或所有数字字母字符等的匹配。为更方便工作,可以使用预定义的字符集,称为字符类。
类 | 说明 |
---|---|
[:alnum:] | 任意字母和数字(同[a-zA-Z0-9] ) |
[:alpha:] | 任意字符(同[a-zA-Z] ) |
[:blank:] | 空格和制表(同[ \t] ) |
[:cntrl:] | ASCII控制字符(ASCII 0到31和127 ) |
[:digit:] | 任意数字(同[0-9] ) |
[:graph:] | 与[:print:] 相同,但不包括空格 |
[:lower:] | 任意小写字母(同[a-z] ) |
[:print:] | 任意可打印字符 |
[:punct:] | 既不在[:alnum:] 又不在[:cntrl:] 中的任意字符 |
[:space:] | 包括空格在内的任意空白字符(同[ \f\n\r\t\v] ) |
[:upper:] | 任意大些字母(同[A-Z] ) |
[:xdigit:] | 任意十六进制数字(同[a-fA-F0-9] ) |
7、匹配多个实例
目前为止使用的所有正则表达式都试图匹配单次出现。如果存在一个匹配,该行被检索出来,如果不存在,检索不出任何行。但有时需要对匹配的数目进行更强的控制。
例如,你可能需要寻找所有的数,不管数中包含多少数字,或者你可能想寻找一个单词并且能够适应一个尾随的s(如果存在),等等。
这可以用列出的正则表达式重复元字符来完成。
元字符 | 说明 |
---|---|
* | 0个或多个匹配 |
+ | 1个或多个匹配(等于{1,} ) |
? | 0个或1个匹配(等于{0,1} ) |
{n} | 指定数目的匹配 |
{n,} | 不少于指定数目的匹配 |
{n,m} | 匹配数目的范围(m不超过255) |
select prod_name
from products
where prod_name regexp '\\([0-9] sticks?\\)'
order by prod_name;
sticks?
匹配stick
和sticks
(s
后的?
使s
可选,因为?
匹配它前面的任何字符的0
次或1
次出现)。
例如,匹配连在一起的4个数字。
select prod_name
from products
where prod_name regexp '[[:digit:]]{4}'
order by prod_name;
如前所述,[]
匹配任意数字,因而它为数字的一个集合。{4}
确切地要求它前面的字符(任意数字)出现4次,所以[[]]{4}
匹配连在一起的任意4位数字。
需要注意的是,在使用正则表达式时,编写某个特殊的表达式几乎总是有不止一种方法。
select prod_name
from products
where prod_name regexp '[0-9][0-9][0-9][0-9]'
order by prod_name;
8、定位符
目前为止的所有例子都是匹配一个串中任意位置的文本。为了匹配特定位置的文本,需要使用定位符。
元字符 | 说明 |
---|---|
^ | 文本的开始 |
$ | 文本的结尾 |
[[:<:]] | 词的开始 |
[[:>:]] | 词的结尾 |
例如,如果你想找出以一个数(包括以小数点开始的数)开始的所有产品。
select prod_name
from products
where prod_name regexp '^[0-9\\.]'
order by prod_name;
^
的双重用途在集合中(用[
和]
定义),用它来否定该集合,否则,用来指串的开始处。
使regexp
起类似like
的作用like
和regexp
的不同在于,like
匹配整个串而regexp
匹配子串。利用定位符,通过^
开始每个表达式,用$
结束每个表达式,可以使regexp
的作用与like
一样。
简单的正则表达式测试可以在不使用数据表的情况下用select
来测试正则表达式。regexp
检查总是返回0(没有匹配)或1(匹配)。可以用带文本串的regexp
来测试表达式,并试验它们。相应的语法如下:
select 'hello' regexp '[0-9]';
这个例子显然将返回0(因为文本hello
中没有数字)。
?