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