Regular Expression 在網頁資料擷取上的應用(上) 作者: Sigma+

壹、 前言

Internet是個巨無霸的資料貯藏庫,存放著各式各樣的資料,然而如何從龐大的資料量當中,將分散且不完整的資料擷取出適合與想要的資料,做好知識管理以應付瞬息萬變的環境挑戰,同時減低企業的人力資源投資,一直是各方重視的一個課題。透過全球資訊網(World Wide Web)取得心目中想要的資料,長久以來是大家所採行的方法之一,Web-Based的WWW內容是以HTML編寫而成,裡面包含著許多有用的資料,但HTML不同於 XML 這種對格式要求非常嚴格的標籤語言,要在網頁中精確地獲取HTML包含的資料,是件相當令人頭疼的事情,本文的主要目的在介紹如何利用Java與Regular Expression解析整理出其中的數據資料,在本期將先介紹Regular Expression表示式的規則與JDK 1.4的regex套件,下期將再介紹如何結合兩者應用在網頁資料的擷取。


貳、 Regular Expression背景知識

現今的網頁內容是基於 HTML 的格式進行編寫,HTML是"呈現導向"(Presentation-Oriented)的語言,瀏覽器會根據HTML語法忠誠地呈現內容,且瀏覽器在經過多年發展其適應能力越來越強,許多無雜亂無章的格式,一樣能呈現出令人滿意的內容。在以XML格式作為製作網頁內容的方式還未全面普及前,想要解析出HTML其中的內容的確相當費功夫,目前以Java語言來說,最容易可找到的方法不外乎是HTML Parser跟Regular Expression兩種,本文在此將介紹以JDK 1.4所提供的java.util.regex套件為工具,解析擷取網頁上HTML內的資料。

Regex套件是JDK1.4版本才推出的一個套件,之前的程式設計師若想要使用Regular Expression作為撰寫程式時解決問題的方法,必須採用Jakarta ORO、Jakarta Regex、JRegex、GNU、Pat等等,在從使用上的方便性、套件彈性、普遍性、日後的維護性、效率以及Unicode的支援等角度考量[1],採用昇陽JDK 1.4所提供的java.util.regex是本文極力推薦的原因。

Regular Expression一直以來只有少數在使用的人才認識它,對於它始終很陌生不知是什麼;「Regular Expression」可以譯做「正則表示式」,主要用途在於利用字元字串的特徵(Pattern)對文件內文做比對搜尋,甚至是替換,因此,Regular Expression是一套規則,一種可以與其他語言或工具相結合發揮組合相乘功效的表達方法。Regular Expression最早是由數學家Stephen Kleene於1956年提出,後來在資訊領域廣為應用,現在已經成為ISO(國際標準組織)的標準之一。Regular Expression具有兩種標準:基本的正則運算式(BRE),擴展的正則運算式(ERE)。ERE包括BRE功能和另外其他的概念。

Regular Expression經過幾個時期的發展後,逐漸被廣泛的應用,UNIX平臺下的vi,程式語言Perl、PHP以及將在文章後面介紹的Java,都納入這套規則於語言當中,目前在許多網路上的搜尋引擎、E-mail驗證程式,甚至是做HTML裡hyperlink的搜尋也都可看見它的影子。Regular Expression的應用大致可分為四類[2]:
1. 資料的解析
2. 資料的驗證
3. 字元序列的處理
4. 資料的擷取與報表產生


參、 Regular Expression的組成

Regular Expression表示式內容的組成主要有兩種,分別是:
(1) 一般字元(Characters):其代表意義與欲找尋字元之字面意義相同。
(2) 運算(操作)元(Operators):用以表示某一規則的意義,主要用以作為找尋比對時的特殊控制字元。

以下介紹幾個常用Regular Expression語法的規則[3]:

1. 單一字元(single character):例如一Regular Expression "P",那麼任何包含P字元的句子,都會被找出,如"People"、"Person"但不會找出"pig"。
2. . (any character):.可用以代表任意的字元,例如一Regular Expression ".t.m",那麼"atom"、"item"跟"stem"都會被比對找出。
3. + (Kleene plus):用以代表一個或以一個以上的字元,例如可以Regular Expression "10+" 比對找出"10"、"100"跟"1000",但不會比對找出"1"。
4. * (Kleene star):用以代表零個或以零個以上的字元,例如可以Regular Expression "10*" 比對找出"1"、"10"跟"100"。
5. ? (question symbol):用以表示當句子中如果存在某一字串或某一字元的話,此一字串或此一字元恰出現一次的情況,例如可以Regular Expression "?erd",比對找出"berd"、"herd"與"erd"。
6. ^ (caret):用以比對一句子的開頭,例如一Regular Expression "^CAPITAL",可以比對找出句子"CAPITAL's signify emphasized speech, anger or SHOUTING",而不會比對找出"Your such a CAPITAL idiot!"。
7. $ (dollar symbol):用以比對一句子的結尾,例如一Regular Expression "here$",可以比對找出"I like it here",而不會比對找出"here is a potato."。
8. \ (escape):用以比對句子中代表某種意義的字元時,需在此字元前加上一反斜線,如:尋找文章中含有*的句子,可以Regular Expression " \*",作為尋找比對的特徵。
9. [] (ranges):用以比對中括弧內任何的字元或字串,例如:Regular Expression "1[123]512",會比對出"11512"、"12512" 與 "13512"。
10. () (group):Regular Expression可將字串或字元集組合成一個個基本的元素(element、atom),以(小)括弧包起來,然後以此元素作為比對的特徵;例如:Regular Expression "(eLand) ( ) (is)",可比對找出句子"eLand is excellent!"。
11. | (alternatives):此一符號相當於OR運算,在文章句子當中找出符合其中的選項,例如:Regular Expression "(cat|dog|bird)",可比對找出句子"The pet store sold cats, dogs, and birds."中的"cat"、"dog"與"bird"。
12. {n}、{n, m} (repetition):使用Regular Expression時可以指定欲尋找的任意字元或字元集出現的次數,例如:"a{5} b{,6} c{4,8}",找出符合的結果會有,"aaaaa"、"bbbbb"與"ccccc"。
13. ^ (complement、negation):在Regular Expression ^的另一個意義是作為"否"-not的意義,例如:Regular Expression " [^a-z]a",在句子"Mary had a little lamb. And everywhere that Mary went, the lamb was sure to go."中,只會找出粗體字的部分。

Regular Expression除了以上常用的規則外,以下簡式是經常被使用到的幾個例子。

1. \d 等於 [0-9] 數字。
2. \D 等於 [^0-9] 非數字。
3. \s 等於 [ \t\n\x0B\f\r] 空白字元。
4. \S 等於 [^ \t\n\x0B\f\r] 非空白字元。
5. \w 等於 [a-zA-Z_0-9] 數字或是英文字。
6. \W 等於 [^a-zA-Z_0-9] 非數字與英文字。

肆、 Regular Expression 與Java語言

Java在JDK 1.4之後開始支援正則表示式,提供java.util.regex套件,給程式設計師在撰寫程式時,利用Regular Expression處理一些字元字串的格式比對問題,在1.4 API的手冊上,可發現到match()、replaceAll()等方法已可接受Regular Expression當作參數傳入,讓Java語言在String上的處理可以更強大也更為細緻(fine-grained)。

在regex套件下,有Pattern類別與Matcher類別,可根據Regular Expression規則,提供字元字串比對處理的方法;Pattern類別主要用以將字串或字元集等字元序列正則表示化,亦即將字串形式的字元序列特徵化規則化,作為比對的一個樣版;Matcher類別則作為將經正則表示化的字元序列(樣版或模版,template)進行序列比對。

使用java.util.regex套件做資料的比對、搜尋、擷取或取代等處理,大致可分為以下步驟[4][5]:
1. 產生Pattern
將Regular Expression表示式以方法compile()編譯成一個Pattern的實體(instance)。
2. 產生一個對應於Pattern的Matcher
利用步驟一Pattern物件的方法matcher()去產生一個Matcher物件。
3. 利用Matcher去搜尋或處理資料
利用Matcher的方法去做資料的處理,例如:方法find()。

以下是一個使用java.util.regex例子,根據以上三步驟而走。

public class RegExprTest {

public static void main(String[] args) throws Exception {

String text = new String("One cat, two cats, how fun.");

//產生Pattern

Pattern p = Pattern.compile("cat");

//產生一個對應於PatternMatcher

Matcher m = p.matcher(text);

//利用Matcher去搜尋或處理資料

boolean result = m.find();

while(result) {

System.out.println(m.group());

result = m.find();

}

}

}

在步驟一產生Pattern同時,可以在編譯的方法compile()指定一個flag,決定Regular Expression表示式是否為CASE_INSENSITIVE、UNICODE_CASE,因為java.util.regex對字串的預設處理為CASE_SENSITIVE跟ASCII。
例如:
Pattern p = Pattern.compile("cat", Pattern.CASE_SENSITIVE | UNICODE_CASE);
式子中的 | 是OR的意思,可以讓Regular Expression的使用藉著flag設定更為靈活。

步驟二是以方法matcher(charsequence)去產生一個Matcher物件,方法的資料來源是charsequence,因為charsequence是一個readable sequence of characters的介面,所以,對於實做此介面的String、StringBuffer跟CharBuffer類別等的資料型態的資料亦可被接受。

到了步驟三,資料的搜尋跟處理方法,java.util.regex有提供以下幾類[6]:
1. Exact Match:boolean matches()、boolean matches(String regex, CharSequence input)。
2. Partial Match:boolean lookingAt()、boolean find()。
3. Capturing groups:int end()、int end(int group)、String group()、String group(int group)、int groupCount()、int start()、int start(int group)。
4. String Replacement:String replaceAll(String replacementStr)、String replaceFirst(String replacementStr)。
5. String Tokenizing:String [] split(CharSequence inputStr)。

伍、 Regular Expression參考工具

經常會有對於自己所寫出的Regular Expression會有疑問,或是想對於自己寫出的Regular Expression做驗證,甚至是進一步的重新定義(Re-define),讓寫出的Regular Expression可以更為精確的找出想要的目標資料,建議不妨嘗試找找網路上提供的一些免費資源,作為自己在使用Regular Expression時的輔助工具,尤其是對於Regular Expression的初學者而言,會有更大的一些助益。以下所列是網路上能找到的免費工具,可以試試。
1. Expresso http://www.ultrapico.com/ExpressoDownload.htm
2. Regulator http://royo.is-a-geek.com/iserializable/regulator/
3. RegexBuilder http://renschler.net/regexbuilder/
4. FindIt http://bizilogic.com/findit/


陸、 結語

利用Regular Expression將字元序列予以格式化(formation),做資料搜尋、取代、擷取等用途的處理,是一種以規則為基礎(rule-based)的資料處理技術,因此,只要使用者能夠寫出合適的Regular Expression,對於資料的處理也更能夠得心應手,再加上適當的工具做搭配,例如:java.util.regex,即可做出許許多多不錯的應用。



參考資料

[1] Jeffrey E. F. Friedl, Mastering Regular Expressions, 2nd Edition, O'Reilly, Cambridge, MA, July 2002.
[2] Hetal C. Shah, Regular Expressions in J2SE, ONJava.com, http://www.onjava.com/pub/a/onjava/2003/11/26/regex.html.
[3] Using regular expressions, http://www-128.ibm.com/developerworks/edu/l-dw-linuxregexp-i.html.
[4] Dana Nourie and Mike McCloskey, Regular Expressions and the Java Programming Language, http://java.sun.com/developer/technicalArticles/releases/1.4regex/
[5] Java 2 Platform API Specification
[6] Java技術論壇-正則運算式,http://www.javaworld.com.tw/jute/post/view?bid=5&id=91&sty=3&keywords=%A5%BF%AB%68%B9%42%BA%E2%A6%A1
.

© Copyright by 2003 eLand Technologies Co., LTD . All rights reserved. 意藍科技股份有限公司