也是在 Tim Club 回答的問題,記在這裡自己備忘用。
一位網友問到,想把以下的文字:
1 2 3 4 | |
換成:
1 2 3 4 | |
其實這個就是所謂的 BBCode ,只是我很少看過第一種表示形式。
怎麼轉換呢?
首先可以先想到的,是用替換的方式,也就是 str_replace() 。不過這裡有個問題,那就是標籤名稱後的冒號與英數混合字串是非固定的,所以我們就不能用 str_replace() 這種單一的替代函式,要另外想辦法解決。
雖然說是不固定的文字,但總是能找到一點脈絡,其組合大概能分成以下幾種:
1
| |
1
| |
1
| |
轉換後就要變成:
1
| |
不過 color 標籤比較特別,要轉換成:
1
| |
像這樣有規則可循的替換,我們就能使用 Regular Expression (正規表示式) 來完成它。
在 Regular Expression 裡,替換函式是 preg_replace() ,所以我寫了以下的程式測試:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
preg_replace() 第一個參數是要搜尋的 Pattern ,可以是一個字串,也可以是陣列;第二個參數則是要替換的文字,一樣可為字串或陣列。當兩個參數皆使用陣列時, preg_replace() 會幫我們以一對一的方式將 $pattern 陣列中比對到的文字,用 $replace 陣列來替換掉。
回到上面的程式,第一個 Pattern 是:
1
| |
我先比對前面的標籤好了,首先是左方括號 \[ ,因為它屬於特殊字元,所以要用反斜線將它脫逸 (Escape) ;然後是標籤 ([a-z]+) ,再比對可有可無的屬性 ([=\w]*) ,接著是冒號加上英數混合字串 [\w]* ,最後是右方括號 \] ;這樣就會比對出 [code:07318bb522] 、 [color=blue:8841ec98ad] 及 [b:8841ec98ad] 。
而中間的文字是可有可無的,我就用 (.*?) 來將它比對出來。
最後是後面的標籤,方括號、冒號及英數混合字串是和上面一樣的比對方式;然而這裡用到一個 \1 ,指的是把前面第一個小括號裡所比對到的文字拿來再比一次,這就是所謂的 Back references (中文我不會翻) ;也就是說,如果我前面比對到的標籤名稱是 color ,那麼 \1 就會再用 color 來比對,以達到兩兩對稱的目的。
那第二個 Pattern 是作什麼用的?不是還有一個 quote 標籤還沒比到嗎?我比較笨,在第一個 Pattern 中我並沒辦法完全比對到 quote="Anonymous" ,所以我只好透過第二個 Pattern 來將它比對出來。
1
| |
只看前面的標籤就行了,後面的標籤和第一個 Pattern 是一樣的。略過方括號不提,先用 ([a-z]+) 比對標籤名稱,然後是等號 = ,接著是 \"[\w]*\" 比對出帶有雙引號的屬性。
比對完之後,接著就是要將它替換掉了。在替換的字串裡,我們不需要再對特殊字元做脫逸,不過 Back references 仍然有用。 Back references 會對應到 Pattern 內有小括號的地方,然後依次替換,所以對第一個 Pattern ,我就用以下的字串來替換:
1
| |
這時候 \1 就是標籤名稱, \2 則是等號及屬性, \3 當然就是中間的文字。
而第二個 Pattern 就用以下字串替換:
1
| |
原理就和上面是一樣的。
當然以上的替換方式還不是很周全,應該有很多狀況無法處理。不過我想基本原理掌握後,應該能夠舉一反三吧。
請大家指教。