一、正則表達式
1、正則表達式介紹
前例博客中的過濾例子允許用匹配、比較和通配操作符尋找數據。對於基本的過濾,這樣就足夠了。但是隨着過濾條件的複雜性的增加,WHERE子句本身的複雜性也有必要增加。
這也就是正則表達式變得有用的地方,正則表達式(Regular Expression)是用來匹配文本的特殊的串(字符集合)。如果你想從一個文本文件中提取電話號碼,可以使用正則表達式。如果想替換一個頁面中的所有URL爲這些URL的實際HTML鏈接,也可以使用一個正則表達式。所有程序設計類的語言、文本編輯器、操作系統等都支持正則表達式。
正則表達式用正則表達式語言來建立,正則表達式語言是用來完成剛討論的所有工作以及更多工作的一種特殊語言。與任意語言一樣 ,正則表達式具有你必須學習的特殊語法和指令。
2、使用MySQL正則表達式
那麼,正則表達式和MySQL有什麼關係呢?已經說過,正則表達式的作用是匹配文本,將一個模式與一個文本串進行比較。MySQL用WHERE子句對正則表達式提供了初步的支持,允許你指定正則表達式過濾SELECT檢索出的數據。
2.1基本字符匹配
我們從一個簡單的例子開始,下面的語句檢索列prod_name包含文本1000的所有行。
MariaDB [course]> SELECT prod_name,prod_id
-> FROM products
-> WHERE prod_name REGEXP '1000'
-> ORDER BY prod_name;
+--------------+---------+
| prod_name | prod_id |
+--------------+---------+
| JetPack 1000 | JP1000 |
+--------------+---------+
1 row in set (0.00 sec)
這條語句除了關鍵字LIKE被REGEXP替代外,看上去非常像使用LIKE的語句。它告訴MySQL:REGEXP後跟的東西作爲正則表達式處理。
在上面的例子中,正則表達式或許沒有帶來太多好處,可能還會降低性能,但請參考下面的例子:
MariaDB [course]> SELECT prod_name,prod_id
-> FROM products
-> WHERE prod_name REGEXP '.000'
-> ORDER BY prod_name;
+--------------+---------+
| prod_name | prod_id |
+--------------+---------+
| JetPack 1000 | JP1000 |
| JetPack 2000 | JP2000 |
+--------------+---------+
2 rows in set (0.00 sec)
這裏使用了正則表達式'.000'
。.
是正則表達式語言中一個特殊的字符.。它表示匹配任意一個字符,因此1000和2000都匹配且返回。
LIKE與REGEXP的區別:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name LIKE '1000'
-> ORDER BY prod_name;
Empty set (0.00 sec)
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '1000'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
+--------------+
1 row in set (0.01 sec)
上述例子發現第一條語句不返回數據,而第二條語句返回一行。爲什麼呢?
正如之前講通配符時所說,LIKE匹配整個列。如果被匹配的文本在列值中出現(被匹配文本'1000'
,列值'JetPack 1000'
),LIKE將不會找到它,相應的行也不被返回(除非使用通配符)。而REGEXP在列指內進行匹配,如果被匹配的文本在一個列值中出現,REGEXP將會匹配到它,相應的行將會被返回。這是一個很重要的差別。
那麼,REGEXP能否匹配整個列值呢(從而和LIKE起到相同的作用),答案是肯定的,使用定位符^
和$
即可。
匹配不區分大小寫:MySQL中的正則表達式匹配(自從3.23.4版本之後)不區分大小寫(即大寫和小寫都匹配)。爲區分大小寫,可使用
BINARY
關鍵字,如WHERE prod_name REGEXP BINARY 'Jetpack .000'
2.2進行OR匹配
爲搜索兩個串之一,或者爲這個串,或者爲另一個串。使用|
,如下所示:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '1000|2000'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)
語句中使用了正則表達式1000|2000
。
|
爲正則表達式的OR操作符。它表示匹配其中之一,因此1000和2000都匹配並返回。
使用|
從功能上類似於在SELECT語句中使用OR語句,多個OR條件可併入單個正則表達式。
兩個以上的OR條件,例如
'1000|2000|3000'
將匹配1000或2000或3000
2.3匹配幾個字符之一
匹配任何單一字符,但是,如果你只想匹配特定的字符,怎麼辦?
可以通過指定一組用[]
括起來的字符來完成,如下所示:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '[123] Ton'
-> ORDER BY prod_name;
+-------------+
| prod_name |
+-------------+
| 1 ton anvil |
| 2 ton anvil |
+-------------+
2 rows in set (0.00 sec)
這裏使用了正則表達式[123]Ton
。[123]
定義一組字符,它的意思是匹配1或2或3 。
因此,1ton
和2ton
都匹配且返回。
正如所見,[]
是另一種形式的OR
語句。事實上,正則表達式[123]Ton
是[]1|2|3]Ton
的縮寫,也可以使用後者。但是,需要用[]
來定義OR
語句查找什麼 。具體請看下例:
MariaDB [course]> 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) |
+---------------+
5 rows in set (0.00 sec)
這並不是希望的輸出,兩個要求的行被檢索出來,但還檢索出了另外3行。之所以這樣是由於MySQL理解你的意思爲’1’或’2’或’3 ton’。除非把字符|
括在一個集合中,否則它將應用於整個串。
字符串也可以被否定,它們將匹配除指定字符外的任何東西。爲了否定一個字符集,在集合的開始處放置一個^
即可。所以[^123]
匹配除了這些字符之外的任何內容。
2.4匹配範圍
集合可以用來定義要匹配的一個或者多個字符。
例如 ,下面集合將匹配數字0到 9 [0123456789]
爲了簡化這種類型的集合,可以使用-
來定義一個範圍。下面的式子功能等同於上面的數字列表[0-9]
範圍不限於完整的集合,[1-3]和[6-9]也是合法的範圍,此外,範圍也不一定是數值的,也可以是字母的。
[a-z]
就代表匹配任意字符 。
舉例:
MariaDB [course]> 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 |
+--------------+
3 rows in set (0.00 sec)
這裏使用正則表達式[1-5] Ton
,[1-5]
定義了一個範圍,這個表達式意思是匹配1到5,因此返回3個匹配行,由於5 Ton
匹配,所以返回.5 Ton
2.5匹配特殊字符
正則表達式語言由具有特定含義的特殊字符構成,已經學習了. [] | -
等,還有其他一些字符串。
如果要匹配這些字符的話,應該怎麼辦?
假如,要找出包含.
字符的值,該如何搜索。請看下例:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '.'
-> ORDER BY prod_name;
+----------------+
| prod_name |
+----------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
| Bird seed |
| Carrots |
| Detonator |
| Fuses |
| JetPack 1000 |
| JetPack 2000 |
| Oil can |
| Safe |
| Sling |
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
14 rows in set (0.00 sec)
這並不是期望的輸出,.
匹配任意字符。因此每行都被檢索出來。
爲了匹配特殊字符,必須用\\
爲前導。\\-
表示查找-
,\\.
表示查找.
修改上例爲
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '\\.'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| .5 ton anvil |
+--------------+
1 row in set (0.00 sec)
這纔是期望的輸出,這種處理就是所謂的**轉義(escaping)。*,正則表達式內具有特殊含義的所有字符都必須以這種方式轉義。
\\
也可以用來引用元字符(具有特殊意思的字符),如下表所示:
爲了匹配反斜槓字符本身,需要使用\\\
2.6匹配字符類
存在找出你自己經常使用的數字、所有字母字符或所有數字字母字符等的匹配。爲了更方便工作,可以使用預定義的字符集,稱爲字符類(character class)。
下表列出字符類以及它們的含義
2.7匹配多個實例
目前爲止使用的所有正則表達式都試圖匹配單次出現,如果存在一個匹配,該行被檢索出來,如果不存在,檢索不出任何行,但有時需要對匹配的數目進行更強的控制。例如,你可能需要尋找所有的數,不管數中包含多少數字,或者你可能想尋找一個單詞並且能夠適應一個尾隨的s。
可以用下表列出的正則表達式重複元字符來完成。
看這個例子:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '\\([0-9] sticks?\\)'
-> ORDER BY prod_name;
+----------------+
| prod_name |
+----------------+
| TNT (1 stick) |
| TNT (5 sticks) |
+----------------+
2 rows in set (0.00 sec)
解析:正則表達式\\([0-9] sticks?\\)
,分開來看\\(
匹配(
,[0-9]
匹配任意數字,sticks?
匹配stick
和sticks
。(s後的?使s成爲可選的,因爲?匹配它前面的任何字符的0次或1次出現)。\\)
匹配)
。
以下是另一個例子,這次我們打算匹配連在一起的4位數字:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '[[:digit:]]{4}'
-> ORDER BY prod_name;
+--------------+
| prod_name |
+--------------+
| JetPack 1000 |
| JetPack 2000 |
+--------------+
2 rows in set (0.00 sec)
分析:[[:digit:]]
表示數字,因而它爲數字的一個集合。{4}
確切地要求它前面地字符(任意數字)出現4次,所以[[:digit:]]{4}
匹配連在一起的任意4位數字。
需要注意的是,編寫正則表達式的時候,某個特殊的表達式幾乎不止一種寫法,上面的例子也可以寫成下面的語句:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '[0-9][0-9][0-9][0-9]'
-> ORDER BY prod_name;
2.8定位符
目前爲止所有的例子都是匹配一個串的任意位置的文本,爲了匹配特定位置的文本,需要使用下面列出的定位符。
例如 ,如果你想找出一個數(包括以小數點開始的數)開頭的所有產品,簡單搜索[0-9\\.]
或者[[:digit:]\\.]
是不行的,因爲它將在文本內任意位置進行匹配。解決辦法是使用定位符^
,如下:
MariaDB [course]> SELECT prod_name
-> FROM products
-> WHERE prod_name REGEXP '^[0-9\\.]'
-> ORDER BY pord_name;
+--------------+
| prod_name |
+--------------+
| .5 ton anvil |
| 1 ton anvil |
| 2 ton anvil |
+--------------+
3 rows in set (0.00 sec)
簡單的正則表達式的測試:
可以在不使用數據庫表的情況下用SELECT來測試正則表達式。REGEXP檢查總是返回0(沒有匹配)或1(匹配)。用帶文字串的REGEXP來測試表達式,並試驗它們。相應的語法如下:
SELCT 'hello' REGEXP '[0-9]';
+------------------------+
| 'hello' REGEXP '[0-9]' |
+------------------------+
| 0 |
+------------------------+
1 row in set (0.00 sec)
這個例子返回值爲0,因爲文本hello中沒有數字。