說起Regex,可以一直上溯至對人類神經系統如何工作的早期研究。Warren McCulloch和Walter Pitts這兩位神經生理學家研究出一種數學方式來描述這些神經網路。
1956
年,一位叫Stephen
Kleene的美國數學家在McCulloch和Pitts早期工作的基礎上,發表了一篇標題為“神經網事件的標記法”的論文,引入了Regex的概念。
Regex就是用來描述他稱為“正則集的代數”的運算式,因此採用“Regex”這個術語。
隨後,發現可以將這一工作應用於使用Ken Thompson的計算搜尋演算法的一些早期研究,Ken Thompson是Unix的主要發明人。Regex的第一個實用應用程式就是Unix中的qed編輯器。
這個專題主要是為了提高大家對Regex的認識和進階的使用。
1 使用Regex來檢測HTML是否關閉
function check_html($html) {
preg_match_all("/<([a-zA-Z0-9]+)//s*[^///>]*>/",$html,$start_tags);
preg_match_all("/<///([a-zA-Z0-9]+)>/", $html, $end_tags);
if(count($start_tags[1]) != count($end_tags[1])) return false;
for($i = 0; $i < count($start_tags[1]); $i++) {
if(!in_array($start_tags[1][$i], $end_tags[1])) return false;
}
return true;
}
解釋:
/<
([a-zA-Z0-9]+)//s*[^///>]*>/這個模式是用來匹配HTML的標記(如:<head>、<
div>、<div
id="main">等等,但是除了<br/>這種)的,並且在$start_tags保持著標籤的名字(如:head、div等)。
而/<///([a-zA-Z0-9]+)>/這個模式是用來匹配閉合的HTML標記(如:</head>,
</div>等)的。並且在$end_tags中保持這閉合的標籤名。然後我們用count($start_tags[1]) !=
count($end_tags[1])這個條件陳述式來判斷開始的標記跟閉合的標記是否相等,不相等就說明沒閉合。最後用in_array($
start_tags[1][$i], $end_tags[1])來判斷開始標跟閉合的標記是否相等。至此,我們就完成了HTML的匹配了!
2 匹配E-mail格式
function check_email($email) {
if(preg_match("/^[/w/d!#$%&'*+-//=?^`{|}~]+(/.[/w/d!#$%&'*+-//=?^`{|}~]+)*@([a-z/d][-a-z/d]*[a-z/d]/.)+[a-z][-a-z/d]*[a-z]$/",
$eamil)) return true;
return false;
}
解釋:
不要被
/^[/w/d!#$%&'*+-//=?^`{|}~]+(/.[/w/d!#$%&'*+-//=?^`{|}~]+)*@([a-z
/d][-a-z/d]*[a-z/d]/.)+[a-z][-a-z/d]*[a-z]$/這條模式給嚇跑了,其實也很簡單的。前部分[/w/d!#$
%&'*+-//=?^`{|}~]+(/.[/w/d!#$%&'*+-//=?^`{|}~]+)*只是匹配符合RFC-2882標
準的E-mail地址允許出現的字元,就是英文字母,數字跟一些符號,有興趣可以查詢RFC-2882手冊,而([a-z/d][-a-z/d]*[a-
z/d]/.)+就是匹配HOST的。最後[a-z][-a-z/d]*[a-z]就是匹配頂級網域名稱的(如:.com、.org)。
3 非貪心模式
有時候使用Regex的時候,你會發現有以下的問題:
preg_match('/".*"/', 'Tony say:"hello", Jack say: "Hi"', $matches);
print_r($matches);
很驚奇的你會發現匹配的是這樣的內容"hello", Jack say: "Hi",而不是"hello"和"Hi"。這就是貪心匹配引起的。在貪心匹配中,Regex會儘可能的匹配最多的字元,所以出現了第一種情況,但是我們想要的是第二種情況,那我們要怎麼做呢?
我們可以使用非貪心匹配,將剛才的模式改為/".*?"/這樣就可以看見我們想要的結果了。*?這個就是非貪心匹配模式。還有另外一種就是+?。
*?:前面的字元可以出現任意多次,但是遇到*?後的一個字元即停止匹配。
+?:前面的字元可以出現一次或者多次,但是遇到+?後的一個字元即停止匹配。
4 檢測一個使用者密碼是否安全
function is_good_pw($pw) {
if(preg_match('/(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,16}/', $pw)) {
return true;
}
return false;
}
解釋:
在
本例中,我們使用了/(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,16}/模式來匹配我們的密碼。在這個模式中,我們使
用了Regex中的前瞻模式(?=)。(?=.*[0-9])這個是匹配密碼中有數字,而(?=.*[a-z])是匹配密碼中有小寫字母,最後(?=.*
[A-Z])就是匹配密碼中有大寫字母。而.{8,16}這個就是匹配密碼是由8至16個字元組成的。那麼我們的密碼就需要有數字,大寫字母和小寫字母組
成的就屬於安全的密碼了~
5 匹配一個網站中的所有連結
function get_links($link) {
$html = file_get_contents($link);
$html = str_replace("/n", "", $html);
$html = preg_replace('/<a/i', "/n<a", $html);
$html = preg_replace('/<//a>/', "</a>/n", $html);
preg_match_all('/<a/s*.*>.*?<//a>/', $html, $matches);
return($matches);
}
在
這個例子中,我們想用file_get_contents來取得一個網頁的內容。然後用str_replace("/n", "",
$html)把所有的換行去掉。再用preg_replace('/<a/i', "/n<a",
$html)和preg_replace('/<//a>/', "</a>/n", $html)來把所有的<a
href=".....">.....</a>模式另起一行。最後就用preg_match_all('/<a/
s*.*>.*?<//a>/', $html,
$matches)匹配連結模式。/<a/s*.*>.*?<//a>/就是匹配<a
href=".....">.....</a>這種模式的Regex。那我們為什麼要把<a
href=".....">.....</a>連結另起一行呢??因為在/<a/s*.*>.*?<//a>
/模式中,.*是不能匹配換行的,所以就如<a>和</a>不在同一行就不能匹配了!!所以我們要這樣做!