| 貳、 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表示式內容的組成主要有兩種,分別是:
(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");
//產生一個對應於Pattern的Matcher
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.