無所不在的 Regexp

好了, 不要一直談理論, 開始動手吧。 要到那裡去下載 regexp 來使用呢? 有 Windows 版嗎?

Regexp 不是一套應用軟體, 而是一小組規則。 支援這一小組規則的軟體, 多到數不清; 不管您用的是任何版本的 Windows, Linux/*BSD, MacOS 等等主流作業系統, 或是 DOS, OS/2, *nix 等等非主流作業系統, 上面都有許多支援 regexp 的軟體。 但是最能夠發揮 regexp 長處的環境, 還是 gnu 命令列環境。 這份講義假設您有這樣的工作環境。 如果您使用 Linux 或 *BSD, 請打開一個終端機 (terminal) 視窗就 ok 啦; 如果您使用 Windows, 請用 pietty 連線到老師準備好的 Linux 帳號; 如果是自修, 沒有 Linux 帳號可用, 則可以下載/燒製/使用 XLiveCD 光碟, 就會出現一個 bash 或 xterm 的命令列視窗。 請用瀏覽器將這篇講義存檔, 現在就拿這個文字檔來實驗, 看看有那些指令/應用軟體支援 regexp。 如果是以 pietty 遠端連線的方式登入, 就必須使用文字瀏覽器 lynx 來下載。

實驗一: 讓一些簡單的 html 標籤反白。 在命令列上打: less -r tools.php 按空間棒向下一頁; 按 b 向上一頁; 按 q 可以跳離; 其他更多控制鍵及基本操作請見 另篇講義。 請按 / 表示搜尋, 再馬上接著按 </?\w+> 注意看看那些東西反白? 這裡的 "\w" 其實是 "[_a-zA-Z0-9]" 的簡寫, 代表 "一個文數字元"; 而 "+" 表示 "前面的樣版可以重複出現 1 次, 2 次, 3 次, ... 任意次"。

用 less 看檔案時, 如果出現亂碼, 可能是編碼設定錯誤。 請改用支援 big5 編碼的終端機, 例如多語版 (multi-lingual) 的 rxvt, mlterm, 或 edubuntu 的 gnome-terminal。 總之要將終端機顯示的編碼改成 big5。 如果是使用 XliveCD/cygwin, 您可以選擇忽略中文亂碼, 反正觀察 html 的標籤才是重點。 如果還是不行, 就要把所有的中文設定清除掉:

        env                             # 列出所有環境變數... 有點太多
        env | grep '_TW'               # 只對 "_TW" 相關的設定有興趣
        unset LANG LANGUAGE             # 逐一清除... 有點辛苦
        env | grep _TW | sed 's/=.*//' # 抓出所有提到 "_TW" 的變數名稱
        unset $(env | grep _TW | sed 's/=.*//')        # 一口氣清除全部

上面我們又看到了兩個支援 regexp 的指令: grep 過濾資訊, 只留下 "含有有趣字串的那些列"; 而 sed 的功用是修改文字, 後面的 s/abc/xy/ 意思是: 把每列上第一次出現的 abc 改成 xy。 這裡的 s/=.*/ 意思是: "把等號 (含) 後面的所有字元全部刪除掉", 其中 . 表示 "任何一個字元" 而 * 表示 " 表示 "前面的樣版可以重複出現 0 次, 1 次, 2 次, ... 任意次"。

更多關於 grep 或 sed 的細節, 請見手冊: man 1 grepman 1 sed 哦, 順便一提, 在查手冊的時候, 您也可以使用 regexp 搜尋字串! (其實它的操作介面就是 less 啦) 例如查看 grep 手冊時, 請搜尋

^       -v
(乘方符號, 七個空格, "-v") 看看 -v 這個選項有什麼作用。 這裡的 ^ 表示: 所搜尋的字串, 必須出現在一列的最前面, 我們才有興趣。

實驗二: 要如何將 (幾乎) 所有的 html 標籤刪除掉? perl -pe 's/<[^>]*>//g' tools.php 這裡的 [^>] 表示 "除了 >" 之外的任何一個字元"; 後面的 g 表示 "一列上所有符合樣版的字串全部都代換掉, 而不是只代換每列的第一串"。 然而這些代換只發生在螢幕上; 原始的檔案並未改變, 不信可以再回頭用 less -r tools.php 檢查看看。 如果要將刪完標籤的結果令存新檔, 就要在上述命令最後加上 " > tools.txt", 再用 less -r tools.txt 檢查結果。 Windows+XLiveCD/cygwin 的使用者, 如果無法直接在命令列視窗看中文, 也可以用這個方法存檔成 /cygdrive/d/abc/def.txt 然後就可以用 wordpad 打開 d:\abc\def.txt 檔案來看。 這個例子可以看到 regexp 通常以列為單位處理資料; 跨列就有點麻煩了, 請自行研究 perlrun 手冊的 -0 選項。 ("-00"; paragraph mode)

實驗三: vim 的使用者, 可以在編輯 tools.php 檔案時, 下 :s/<[^>]*>//g 指令, 就可以直接修改原檔案。 此外, 在 vim 裡面, 也可以按 "/" 鍵搜尋, 搜尋時一樣可以使用 regexp 的特殊符號。 如果希望黃底顯示所有搜尋結果, 可以下 :set hlsearch 指令; 如果只希望移動遊標至搜尋結果, 但不希望黃底顯示, 可以下 :set nohlsearch

喜歡寫程式的人一定要知道: 許多程式語言包含 perl/python/php 等等 scripting 語言 及 c, c++, java 等等傳統語言, 也都有 regexp 函式庫。 懂得善用 regexp 函式庫, 在處理字串時, 您的程式可以比別人短, 功能比別人強, 彈性比別人大。

但是 regexp 絕對不是程式設計師的專利; 它的門檻比寫程式低很多。 連 OpenOffice 甚至是微軟的 Office 等等文書處理軟體裡面也可以使用: (1), (2)。 電腦技巧當中, 除了打字, 還有什麼比 regexp 適用的範圍更廣泛呢? 複雜的中打規則我們都願意學了, 何妨花十分之一不到的力氣, 學學無所不在的 regexp?

當然, 「無所不在」 也帶來一個小小的困擾: 不同的軟體所採用的 regexp 符號有點小小的差異。 例如在 perl 中, 要表達 "前面的樣版可有可無", 用 ? 但在 grep 中, 預設必須用 \? 。 又如在 perl 中, [^0-9a-zA-Z_] ("任一個非文數字元, 例如一個標點符號或一個空白字元等等) 可簡寫成 \W 但在 egrep 中則簡寫成 [^[:alnum]]

這些細微的差異令人頭昏, 不值得深究。 筆者採用懶人的解決方案: 支援 regexp 的軟體當中, 數 perl 的功能最強大, 而它的特殊符號變化規則也最簡單, 學它就很夠用了。 這也是為什麼後來其他語言/環境/工具的 regexp 都以 perl 為標竿 -- 跟 perl 相容的 regexp 語法被稱為 PCRE (perl compatible regular expressions), 儼然成為一個公認的標準。 如果你所使用的工具/軟體不支援 PCRE, 那就常用/常查手冊吧。 (對我而言, 主要是 less 與 vim) 至於不熟悉的就表示不夠常用, 可以忽略。 因此接下來的章節以 PCRE 為主。 我在自己的 ~/.basrh 裡面加上一句:

        alias pgrep='/bin/grep -P --color=auto'
    

這樣以後可以用 pgrep 指令做 PCRE 搜尋, 而且它會用不同的顏色顯示找到的字串, 有助於 regexp 除錯。