用正則表達式進行搜索
1.正則表達式介紹
? 對于基于的過濾(或者甚至是某些不那么基本的過濾),可以用匹配、比較和通配操作符尋找數據,這樣足夠了。但隨著復雜性的增加。WHERE子句的復雜性也有必要增加。
? 這也就是正則表達式變得有用的地方。正則表達式是用來匹配文本的特殊的串(字符集合)。如果你想從一個文本文件中提取電話號碼,可以使用正則表達式如果你需要查找名字中間有數字的所有文件,可以使用一個正則表達式。如果你想在一個文本塊中找到所有重復的單詞,可以使用一個正則表達式。如果你想替換一個頁面中的所有URL為這些URL的實際HTML鏈接,也可以使用一個正則表達式(對于最后這個例子,或者是兩個正則表達式)。
? 正則表達式用正則表達式語言來建立,正則表達式語言是用來完成剛討論的所有工作以及更多工作的一種特殊語言。與任意語言一樣,正則表達式具有你必須學習的特殊的語法和指令。
2.使用MySQL正則表達式
? 那么,正則表達式與MySQL有啥關系?以及說過,正則表達式的作用是匹配文本,將一個模式(正則表達式)與一個文本串進行比較。MySQL用WHERE子句對正則表達式提供了初步的支持,允許你指定正則表達式,過濾SELECT檢索出的數據。
2.1 基本字符匹配
? 下面的語句檢索列prod_name包含文本1000的所有行:
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '1000'
ORDER BY prod_name;
輸出:
prod_name |
---|
JetPack 1000 |
分析:
除關鍵字LIKE被REGEXP替代外,這條語句看上去非常像使用LIKE的語句。它告訴MySQL:REGEXP后所跟的東西作為正則表達式(與文字正文1000匹配的一個正則表達式)處理。
? 為什么要費力地使用正則表達式?在剛才的例子中,正則表達式確實沒有帶來太多好處(可能還會降低性能),不過,請考慮下面的例子:
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '.000'
ORDER BY prod_name;
分析:這里使用了正則表達式.000。.是正則表達式語言中一個特殊的字符。它表示匹配任意一個字符,因此,1000和2000都匹配且返回。
LIKE與REGEXP
在LIKE和REGEXP之間有一個重要的差別。請看以下兩條語句:
SELECT prod_name FROM products WHERE prod_name LIKE '1000' ORDER BY prod_name; SELECT prod_name FROM products WHERE prod_name REGEXP '1000' ORDER BY prod_name;
如果執行上述兩條語句,會發現第一條語句不返回數據,而第二條語句返回一行,為什么?
LIKE匹配整個列,如果被匹配的文本在列值里出現,LIKE將不會找到它,相應的行也不被返回(除非使用通配符)。而REGEXP在列值內進行匹配,如果被匹配的文本在列值中出現,REGEXP將會找到它,相應的行將被返回。這是一個非常重要的差別。
那么,REGEXP能不能用來匹配整個列值(從而起與LIKE相同的作用)?答案是肯定的,使用^和$定位符(anchor)即可
匹配不區分大小寫 MySQL中的正則表達式匹配(自版本3.23.4后)不區分大小寫(即,大小寫都匹配)。為區分大小寫,可使用BINARY關鍵字,如WHERE prod_name REGEXP BINARY 'JetPack .000'。
2.2 進行OR匹配
? 為搜索兩個串之一(或者為這個串,或者為另一個串),使用|,如下所示:
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '1000|2000'
ORDER BY prod_name;
輸出:
prod_name |
---|
JetPack 1000 |
JetPack 2000 |
分析:語句中使用了正則表達式1000|2000。|為正則表達式的OR操作。它表示匹配其中之一,因此1000和2000都匹配并返回。
? 使用|從功能上類似于在SELECT語句中使用OR語句,多個OR條件可并入單個正則表達式。
兩個以上的OR條件 可以給出兩個以上的OR條件。例如,'1000 | 2000 | 3000'將匹配1000或2000或3000。
2.3 匹配幾個字符之一
? 匹配任何單一字符。但是,如果你只想匹配特定的字符怎么辦?可以通過指定一組 [ 和 ] 括起來的字符來完成,如下所示:
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '[123] Ton'
ORDER BY prod_name;
輸出:
prod_name |
---|
1 ton anvil |
2 ton anvil |
分析:這里使用了正則表達式[123] Ton。[123]定義一組字符,它的意思是匹配1或2或3,因此,1 ton和2 ton都匹配且返回(沒有3 ton)。
? 正如所見,[ ]是另一種形式的OR語句。事實上,正則表達式[123] Ton為 [1|2|3] Ton的縮寫,也可以使用后者。但是,需要用 [ ]來定義OR語句查找什么。為更好地理解這一點,請看下面的例子:
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '1|2|3 Ton'
ORDER BY prod_name;
輸出:
prod_name |
---|
1 ton anvil |
2 ton anvil |
JetPack 1000 |
JetPack 2000 |
TNT (1 stick) |
分析:
? 這并不是期望的輸出。兩個要求的行被檢索出來,但還檢索出了另外3行。之所以這樣是由于MySQL假定你的意思是'1'或'2'或'3 ton'。除非把字符|括在一個集合中,否則它將應用于整個串。
? 字符集合也可以被否定,即,它們將匹配除指定字符外的任何東西。為否定一個字符集,在集合的開始處放置一個即可。因此,盡管[123]匹配字符1、2或3,但【123】卻匹配除這些字符外的任何東西。
2.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;
輸出:
prod_name |
---|
.5 ton anvil |
1 ton anvil |
2 ton anvil |
分析:這里使用正則表達式[1-5] Ton。[1-5]定義了一個范圍,這個表達式意思是匹配1到5,因此返回3個匹配行。由于5 ton匹配,所以返回.5 ton。
2.5 匹配特殊字符
? 正則表達式語言由具有特定含義的特殊字符構成。我們已經看到.、[]、|和-等,還有其他一些字符。請問,如果你需要匹配這些字符,應該怎么辦呢?例如,如果要找出包含.字符的值,怎樣搜索?請看下面的例子:
輸入:
SELECT vend_name
FROM vendors
WHERE vend_name REGEXP '.'
ORDER BY vend_name;
輸出:
vend_name |
---|
ACME |
Anvils R Us |
Furball Inc. |
Jet Set |
Jouets Et Ours |
LT Supplies |
分析:這并不是期望的輸出,.匹配任意字符,因此每個行都被檢索出來。
? 為了匹配特殊字符,必須用\ \為前導。\ \ -表示查找-,\ \ .表示查找.。
輸入:
SELECT vend_name
FROM vendors
WHERE vend_name REGEXP '\\.'
ORDER BY vend_name;
輸出:
vend_name |
---|
Furball Inc. |
分析:這才是期望的輸出。\ \ .匹配.,所以只檢索出一行。這種處理就是所謂的轉義(escaping),正則表達式內具有特殊意義的所有字符都必須以這種方式轉義。這包括.、|、[ ]以及迄今為止使用過的其他特殊字符。
? \ \也用來引用元字符(具有特殊含義的字符)
元字符 | 說明 |
---|---|
\f | 換頁 |
\n | 換行 |
\r | 回車 |
\t | 制表 |
\v | 縱向制表 |
匹配\ 為了匹配反斜杠(\)字符本身,需要使用\\。
\或\ ? 多數正則表達式實現使用單個反斜杠轉移特殊字符,以便能使用這些字符本身。但MySQL要求兩個反斜杠(MySQL自己解釋一個,正則表達式解釋另一個)。
2.6 匹配字符類
? 存在找出你自己經常使用的數字、所有字母字符或所有數字字母字符等的匹配。為更方便工作,可以使用預定義的字符集,稱為字符類(character class)。表9-2列出字符類以及它們的含義。
類 | 說 明 |
---|---|
[: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]) |
2.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;
輸出:
prod_name |
---|
TNT (1 stick) |
TNT(5 sticks) |
分析:正則表達式\([0-9] sticks?\)需要解說一下。\(匹配),[0-9]匹配任意數字(這個例子中為1和5),sticks?匹配stick和sticks(s后的?使s可選,因為?匹配它前面的任何字符的0次或1次出現),\)匹配)。沒有?,匹配stick和sticks會非常困難。
? 以下是另一個例子。這次我們打算匹配連在一起的4位數字:
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '[[:digit:]]{4}'
ORDER BY prod_name;
輸出:
prod_name |
---|
JetPack 1000 |
JetPack 2000 |
分析:如前所述,[:digit:]匹配任意數字,因而它為數字的一個集
合。{4}確切地要求它前面的字符(任意數字)出現4次,所以
[[:digit:]]{4}匹配連在一起的任意4位數字。
2.8 定位符
? 目前為止的所有例子都是匹配一個串中任意位置的文本。為了匹配特定位置的文本,需要使用表中的定位符。
? 表 重復元字符
元 字 符 | 說 明 |
---|---|
^ | 文本的開始 |
$ | 文本的結尾 |
[[:<:]] | 詞的開始 |
[[:>:]] | 詞的結尾 |
? 下面舉幾個例子。
輸入:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '^[0-9\\.]'
ORDER BY prod_name;
輸出:
prod_name |
---|
.5 ton anvil |
1 ton anvi |
2 ton anvil |
分析:匹配串的開始。因此,[0-9\ \ .]只在.或任意數字為串中第一個字符時才匹配它們。沒有^,則還要多檢索出4個別的行(那些中間有數字的行)。
^的雙重用途 ^有兩種用法。在集合中(用[和]定義),用它來否定該集合,否則,用來指串的開始出。
使REGEXP起類似LIKE的作用 本章前面說過,LIKE和REGEXP的不同在于,LIKE匹配整個串而REGEXP匹配子串。利用定位符,通過用^開始每個表達式,用$結束每個表達式,可以使REGEXP的作用與LIKE一樣