社群:網頁設計
作者:宥晰
正規表達式提供一種泛用的字串樣式,它是以一組特定字元符號去描述字串樣式的規則。它讓設計師可以先製作範本(pattern)再以範本去比對字串,比對後再視需要對該字串進行操作。常見的操作有:
「格式檢查」、「字串取代」、「字串拆解」。
範本的形式:/正規表達式/[修飾條件]
其中,「/」為分隔符號,將範本置於兩個分隔符號之間以作區隔。右邊分隔符號後方可加註修飾條件(modifier選用),例如:
/\w{3}/i 其後方加了「i」表示不區分大小寫。(比對三個字母或數字)
比對時,將一一取出字串中的每個字元去比對範本,但是什麼情況稱為「匹配」(比對成功),跟誰去匹配呢?匹配的對象稱為字元集合。
範本需要有個方式來表達「字元集合」,換言之,被比對的字元必需符合「字元集合」內的元素才算一次成功的比對。
字元集
字元集的呈現方式是以中括號「[ ]」包含,被拿進來比對的字元若與中括號內的某個字元相同,即算匹配(比對成功)。例:
[abc123],意指:只有a、b、c、1、2、3這六個字元可以比對成功。
也可以使用「否定」的方式呈現字元集,當我們在中括號內的開頭處加上「^」符號時即表示為否定,例:
[^123],意指:非1、2、3的字元都能匹配。
註:「^」這個符號在正規表示式中有兩種完全不一樣用途,別搞混囉!
在字元集開頭處表示否定();在字元集外表示檢查字串開頭處。
(若在中括號裡卻不放在字元集開頭處,則將「^」這符號視為字元集中的一個字元。)
連續性的字元可以用「-」(dash)符號來簡化它。例如:[a-zA-Z.-],意指:所有的大小寫英文字母並外帶「.-」兩個字元。
註:單一字元的字元集,可省略中括號。
一些常用的字元集,PHP提供一些簡化的表達方式:
| [:alpha:] | 所有字母,等於 [a-zA-Z] |
| [:lower:] | 所有小寫字母,等於 [a-z] |
| [:upper:] | 所有大寫字母,等於 [A-Z] |
| [:alnum:] | 所有字母和數字,等於 [a-zA-Z0-9] |
| [:digit:] | 僅數字,等於 [0-9] |
| [:xdigit:] | 僅16進位的字元,等於 [0-9a-fA-F] |
| [:space:] | 所有空白字元,等於 [\n\t\r\x0b] |
| [:punct:] | 所有半形標點符號,同[.,”‘?!;:],(Punctuation symbols) |
| [:blank:] | space and TAB characters only,同 [ \t] (空格與Tab) |
| [:graph:] | 所有的可列印字元,等價於:[^ /t/n/r/f/v] |
| [:print:] | 所有的可列印字元含空格,等價於:[^/t/n/r/f/v] |
| [:cntrl:] | 所有ASCII 0到31之間的控制符 |
註:簡化版的字元集,使用時(在範本中)仍需再加中括號,例:/[[:lower:]]/
table,th,td{border:1px solid gray;}| \s | 表示空白字元,包含\t、\v…等。 |
| \S | 表示非空白字元 |
| \d | 表示所有數字,等於 [0-9] |
| \D | 表示數字以外的字元,等於 [^0-9] |
| \w | 表示所有字母和數字及底線_,等於 [_a-zA-Z0-9] |
| \W | 任何\w以外的字元。[^_a-zA-Z0-9] |
| . | 表示為任意單一字元。( “.” 為英文句號dot). |
重複比對次數(不指定時,預設為{1})
字元集提供範本比對的對象,我們可以進以步地要求匹配的次數。緊接在字元集後方以大括號指定匹配的次數。方式如下:
[字元集]{次數} 或 [字元集]{最小值,最大值}
上例左式,需剛好符合指定次數才算匹配;上例右式是給予一個次數區間,凡符合區間內的次數都算是匹配成功。右式中的最大值與最小值可以省略(仍需保留逗號),其意義為:(設n為整數)
{ ,n} :表示0到n範圍;{ n,} :表示n以上的次數。
其它指定重複次數的符號:(一樣置於字元集合後方)
| ? | 重複0或1次,等同於 {0,1} |
| * | 重複0或多次,等同於{0,} |
| + | 重複1或多次,等同於{1,} |
前面提到的「.」(dot)這個符號,它包含了字元集及次數,代表為任意字元1次。相當於是[^\n]{1}。若需要比對「.」這個符號時,需使用轉義符號「\」,即以「\.」來表示「.」這個符號。「.」的任意字元並不包含換行符號(\n),除非加上/s修飾條件。
註:在中括號(字元集)裡不需使用轉義符,單獨使用時才需轉義,例:
比對URL:/http://(/[\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?/
常用的跳脫字元(用於子表達式內,不使用於字元集([ ])中)
table,th,td{border-collapse:collapse; border:1px solid #000;font-size:12px;text-align:center} td{width:50px}| 原字元 | . | + | * | \ | Enter | Tab | Backspace | Esc |
| 跳脫字元 | \. | \+ | \* | \\ | \n | \t | \r | \e |
註:「()[]{}及|(OR)」這些俱表達式功能的字元也需要使用轉義符號。
定界符號
「^」與「$」這兩個符號為定界符號,「^」只會檢查字串的開頭處,「$」只會檢查字串的結尾處。這兩個符號都置於字元集外,^緊接在左分隔符號(/)後方,將匹配其後面的字元集;$需置於右分隔符號前,將匹配前面的字元集。例如:
/^\d{3}[a-z]+$/ 表示字串以3個數開頭並以英文結尾才算匹配
註:$置於範本中間時無意義,而^在字元集中是否定的意思。
子範本(sub pattern 或稱子表達式)
如果我們把兩個分隔符號(/)間的表達式稱為主範本,在主範本裡我們還可以再以小括號「()」分隔出子範本;換言之,我們可以視為多個子範本組成了一個主範本。
php設有暫存區來存放所有的子範本,我用可以利用\\n或$n來調用第n個子範本所配對成功的字串資料。正是因為可以調用這些暫存資料才讓表達式的實用性顯得更強大了。
當有設置子範本時,程式會依序由左而右將子範本存放在\\1~\\99(或$1~$99)的暫存區中。例如:
/^([A-z])([1-2])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])$/
上例中刻意將身分證號拆分成10個子範本,於是我們可以在\\1~\\10中取得個身分證號的每個字元。而\\0(或$0)也是個暫存區,所存放是主範本所取得的完整字串。(註:「\\」包含了轉義符號)
我們可以取用暫存區的字串作為preg_replace()的取代內容,也可以拿暫存區的內容來調整字元集。要注意的是,拿暫存區內容來調整字元集時需考慮先後次序,也就是說,擺在後面的子範本可以參考前面子範本所取得的內容來調整自己的字元集。
不僅是php的正規表示式有這樣的功能,其實HTML5的表單驗證也支援這樣的功能,參考下例:
<input type=’text’ name=num4′
size=’5′ maxlength=’4′ required
pattern=’ ^(\d)((?!\1)[\d])((?!\1|\2)[\d])((?!\1|\2|\3)[\d])$’ />
上例中的範本(pattern)可以匹配四個相異的數字。後面的子範本參考了前面範本所取得的數字,並要求自己的字元集中不包含前面所取得的數字。這個原本需要動用到javascript的功能,目前HTML5已經幫我們做到了,很多瀏覽器也都支援這個功能。
上例中有使用到「|」這個符號,它的意義即是「或(OR)」的意思。「|」用於子範本中但不用於字元集「[ ]」內。在子範本中使用「|」,表示「擇一」的意思,例:/AA\.(txt|doc)/表示將匹配AA.txt或AA.doc。
上例中還使用到「?!」這個特殊功能,此功能稱為「向前查詢」。
向前查詢及非補獲組 (“?:”、”?=”、”?!” 的應用)
前面說道,每個子範本所取得的子字串都會被存放到暫存區中,我們也可以設定不讓該子字串被放到暫存區裡,即是「非補獲組」。
在小括號中的開頭處加上「?:」時,這個子範本將不被存放到暫存區中,我們將無法使用$n來取得該子範本的內容。除了「?:」不會被存到暫存區中,小括號裡若有「?=」或「?!」的話,也不會存到暫存區。
而「?=」與「?!」是對子字串的開頭字元做了限制,「?=」表示為「取用」(正向查詢)而「?!」表示為「不取用」(負向查詢),例如:
(?=e)(\w*)
表示該子字串需是以「e」開頭的字母或數字;
(?!\1)(\d) 表示這個子字串需不含第一暫存區的字元且必需是數字
註:可以把(?!\1)(\d)這兩個小括號視為一個子範本,因為前面的子範本並不會被存到暫存區裡,而取得的字元仍需符合後面的子範本要求。
操作符號的優先順序
正規表示式有許多操作的符號,確定優先順序也是非常重要:
| 先 | 轉義符號 |
圓方括號 萬用字元 | 限次符號 |
定界符 修飾符 | 或 | 後 |
| \ | ()、[]. | *?+{m,n} | ^、$、/修飾 | | |
註:轉義符號包含\b、\w這類的字元集或單字定界的功能
其它較少使用的控制符號
| \xNN | NN代表兩個16進位的字元。藉此表達在字元集中表達ASCII 字元碼,通過 ASCII 值指定範圍。 |
| \uN | N表示四個16進制的字元。可藉此於字元集中表達Unicode 字元 |
| \b | 於表達式中,表示單字的邊界。(屬於單字的定界符) |
| \B | 與\b相反,表示非單字邊界。例::/\Bapt/ 不能匹配abtitude |
| \w | 表示所有字母和數字及底線_,等於 [_a-zA-Z0-9] |
| \W | 任何\w以外的字元。[^_a-zA-Z0-9] |
| \cX |
X必需為[a-zA-Z]中的字元,不然\c會被視為”c”這個字元。 此為控制字元,代表”control-X”。 javascript做前台操作時才可能會用得到它。php是在伺服器端做字串操作,用不到這個功能。 |
| \e | 匹配escepe鍵。同上,僅前台操作才可能用得上。 |
註:\b在字元集內時[\b]代表退格(同\u0008),此字元打印不出來,一般不太可能會在字元集中使用它。由於符號同於單字定界符,僅需知悉即可。
關於修飾符號(modifier)
在PHP中修飾符有:
「i、m、s、x、e、A、D、S、U、X、J、u」共12種
| i | 不區分英文字母的大小寫。 |
| m |
有換行符號時(\n)才有作用。每一行都會被^、$比對字串頭尾。 若沒設定m時,整個字串只會有一組頭尾。(若設了m則會忽略D) |
| s |
讓「.(dot)」這個符號包含換行符號(\n)。預設”.”不包含\n。 注意,[^a](除了a)這類的否定字元集其實是包含換行符號的。 |
| x |
忽略空白字元。但字元集內或含轉義符的不會被忽略。 “#”這個附註字元,在其後且在換行符號前的內容也會被忽略。 註:取到暫存區的子字串也不會含有空白字元。 |
| A | 強迫定錨(anchor)於字串開頭處。子字串也需從字串開頭處比對。 |
| D | 設定後,字尾界符($)會完全比對字串的最後一字元包含(\n) 一般情況下,會忽略換行字元(即比對到\n的前一字元)。 (不可以m同使使用,若設了m則會忽略D) |
| S | (沒使用過它)。就官網的說法是,設定了此修飾後,會額外對此範本加強分析以提昇分析速度。對於頻繁使用的範本值得附加它。 |
| U |
切換貪婪模式。預設是貪婪模式,但若設了U後會先以非貪婪模式開始,值到遇上”?”後才轉為貪婪模式。 所謂貪婪模式,即是在比對過程中即使比對到完全符合的字串也不會停止比對,它會比對整個的字串,取出較長且符合的字串。 若想在預設貪婪模式中轉為非貪婪模式的話,僅需在範本(pattern)裡加個”?”即可。例如:比對任意字串時,把 /(.*)/ 改為 /(.*?)/ |
註:e僅在preg_replace()使用,會於5.5版出現提醒,並於7.0版移除。
e的修飾會把preg_replace()的取代字串視為php的程式碼求值後再取代。並且會自動為單引號(‘)雙引號(“)倒斜線(\)及NULL加上跳脫符號”\”。
| X |
讓無效的「倒斜線功能」產生錯訊息。例如:\p無功能,於是就產生錯誤訊息。藉此保留此組合以備未來擴充之用。 預設情況下,若沒設置/X,\p會視為”p”這個字元。 |
| u | 將範本及目標字串都視為UTF-8的字碼。註:php5.3.4版後,UTF-8的第五、第六字節的字符會被視為無效字元。(無效字元無法匹配) |
J | 7.2.0版後才可使用的修飾符號。用於改變內部正規表達式的設定,允許子範本有重複的名稱。(實在不曉得這功能怎麼用?) |
註:修飾符號置於右分隔符號後,可以同時置放多種修飾,例:/範本/imU
PHP中關於正規表示式的函式
| int preg_march( 範本, 目標字串, [&結果陣列], [flag=-1], [offset=0] ) |
|
這是字串檢查中最常用的比對函式。雖然它最多可設5個參數,不過,一般只會使用三個參數。範本即是正規表達式,以範本去比對目標字串,若有相符的部分就存入第三參數陣列中。(若僅判斷是否有符合則不需設置第三參數) 回傳值為比對成功的次數,不過一旦成功此函數將停止比對,換言之,此函數只會回傳0或1。若發生錯誤時才會回傳false。 flag 預設值為-1,它有以下兩種設定: PREG_OFFSET_CAPTURE 結果陣列附帶子字串的啟始位置 將把「結果陣列」改為二維陣列,陣列元素都是含2個元素的陣列,兩元素的內容為[0]=>取獲的子字串,[1]=>此子字串的啟始位置。 PREG_UNMATCHED_AS_NULL 讓未匹配以NULL呈現。 原本原匹配會存放空字串,改為存放NULL。(僅影響結果陣列) offset為忽略字元數(以byte計算),換言之,即是設定字串比對的起始位置,若被忽略了三個字元,那麼第四個字元視為字首(可與”^”匹配) |
註:要比對字串到底應改用preg_match_all()
| mixed preg_replace( 範本, 取代式, 目標, [limit=-1], [&$count] ) |
|
函式是以範本(pattern)去選定目標的特定範圍,再以取式去修改它。
範本比對目標時所取得的子字串將依序存放在\\1~\\99區段中,取代式可以利用「\\區段號」或「$1~$99」來提取該區段的字串並組成新字串。 其中,\\0是存放比對成功的完整字串。 limit:設定範本所能產生的子字串上限,預設為-1(不設限) count:計數器,計算取代動作共進行了幾次。(需為變數,將傳址入函式) 回傳值依目標的型態而定,若目標為字串則函式回傳字串,若目標為陣列則函式回傳陣列。若發生錯誤則回傳NULL。 前三個參數既可以是字串也可以是陣列,不全為字串時的處理方式如下: 1、若取代式為字串而範本為陣列時,所有的範本皆依此取式式來處理。 2、若取代式與範本都是陣列時,將依相同索引的取代式去置換同索引的範本所獲取的字串範圍。若範本的元素量多於取代式時,多出來的範本取得字串都將被置換為空字串。 |
修飾符/e即將停用,官方建議改以preg_replace_callback()取代
常遇到的無效修飾錯誤(/d)
錯誤範例
$str = preg_replace(‘/<div> </div>/i’, ”, $str);
正確寫法
$str = preg_replace(‘/<div> <\/div>/i’, ”, $str);
即</div>的收尾的斜線必需要跳脫,不然會誤判為範本的分隔符號。
| preg_replace_callback( 範本, callback, 目標, [limit=-1], [&$count] ) |
| 功能與preg_replace()極為相同。僅取代式改為callback(是個函式)。 此處的callback可以自定函式或無名函式,此函式必需有回傳值。 preg_replace_callback()會將範本所取得的子字串製成陣列,並將此陣列作為callback()的參數;callback()可利用參數的資料來製作取代字串回傳。 註:被帶入callback()中的字串都會自動轉義為跳脫字元。範本設定很容易在此失誤而匹配不到任何字串。 |
超級難,上php.net看例子吧!一堆神人對此函式的用法真是絕妙!!
無名函式(Anonymous functions)的用法可參考下例:(取代為下一年)
$text = “April fools day is
04/01/2002\n”;
echo preg_replace_callback(
“/(\d{2}/\d{2}/)(\d{4})/”,
function($matches) { return $matches[1].($matches[2]+1); }
,$text );
//輸出:April fools day
is 04/01/2003\n
| array preg_split( 範本, 目標字串, [limit=-1], [flags=0] ) |
|
以範本拆分目標字串為一個陣列。範本僅以字串型態表示。 回傳值是個陣列,內容元素為以依範本所收集到的所有子字串。 limit:設定範本所能產生的子字串上限,預設為-1(不設限) flags:有以下三種設定。 PREG_SPLIT_NO_EMPTY 非空字串才回傳。 PREG_SPLIT_DELIM_CAPTURE 空字串也會回傳。 PREG_SPLIT_OFFSET_CAPTURE 回傳陣列的元素值,都是內含兩元素的陣列: [0]=>]字串,[1]=>該字串的起始位置。 |
