正則指引之匹配模式
所謂匹配模式(match mode),指的是匹配時使用的規則。設置特定的模式,可能會改變對正則表達式的識別,也可能會改變正則表達式中字符的匹配規定。常用的匹配模式一共有4種:不區分大小寫模式,單行模式,多行模式,注釋模式。
不區分大小寫模式
在看這個模式的應用實例之前,必須首先了解模式的指定方式。通常,有兩種辦法指定匹配模式:以模式修飾符指定,或者以預定義的常量作為特殊參數傳入來指定。模式修飾符即模式對應的單個字符,使用時將其填入特定結構(?modifier)中(其中的modifier為模式修改符),嵌在正則表達式的開頭。比如不區分大小寫的匹配模式對應的模式修飾符是 i (case Insensitive),對 the 指定此模式,完整的正則表達式就是 (?i)the 。另一種指定模式的方式是使用預定義的常量作為參數,傳入正則函數。在Java中,屬于Pattern類。下表展示了常用語言中不區分大小寫模式的預定義常量:
語言 |
常量 |
.NET |
RegexOptions.IgnoreCase |
Java |
Pattern.CASE_INSENSITIVE |
JavaScript |
/regex/i |
PHP |
/regex/i |
Python |
re.I re.IGNORECASE |
Ruby |
Regexp::IGNORECASE /regex/i |
以Java為例:
//不區分大小寫模式 String text1 = "aaa AAA aaaaa AAAA"; Pattern p1 = Pattern.compile("\\b(a{3,5})\\b",Pattern.CASE_INSENSITIVE); Matcher m1 = p1.matcher(text1); while(m1.find()){ System.out.println(m1.group(1)); }
//不區分大小寫模式(使用模式修飾符) String text1 = "aaa AAA aaaaa AAAA"; Pattern p1 = Pattern.compile("(?i)\\b(a{3,5})\\b"); Matcher m1 = p1.matcher(text1); while(m1.find()){ System.out.println(m1.group(1)); }
單行模式
元字符點號(.)幾乎能匹配任何字符,唯有換行符 \n 是例外。但是,有時候確實需要匹配“任何字符”,比如在處理HTML源代碼時,經常會遇到跨越多行的腳本代碼:
<script type="text/javascript"> ...code.... ...code.... </script>
正則文檔里一般都會說明“點號不能匹配換行符”,不過許多人并不習慣仔細閱讀文檔,所以認為點號 . 能匹配任何字符,當然也就包括換行符,所以直接的想法是用<script\s.*?</script>來匹配。接下來你就會發現最多只能進行到第一行末尾。之前提到過,可以用[\s\S]之類的字符組匹配“任意字符”,所以正則表達式<script\s[\s\S]*?</script>能解決問題。
不過對大多數人來說,點號更加自然,也更簡潔,所以正則表達式提供了單行模式。在這種模式下,所有文本似乎只在一行里,換行符是這一行中的“普通字符”,所以可以由點號 . 匹配。單行模式對應的模式修飾符是 s (Single line),所以如果用模式修飾符,可以在表達式的開頭用(?s)指定:
//單行模式(使用模式修飾符) String text1 = "<script tyte='text/javascript'>dddddddddd\r\nrrrrrrrrrrrr</script>"; Pattern p1 = Pattern.compile("(?s)<script\\s.*?</script>"); Matcher m1 = p1.matcher(text1); while(m1.find()){ System.out.println(m1.group()); }
//單行模式(使用叁數) String text1 = "<script tyte='text/javascript'>dddddddddd\r\nrrrrrrrrrrrr</script>"; Pattern p1 = Pattern.compile("<script\\s.*?</script>",Pattern.DOTALL); Matcher m1 = p1.matcher(text1); while(m1.find()){ System.out.println(m1.group()); }
常用語言中單行模式的預定義常量:
語言 |
常量 |
.NET |
RegexOptions.Singleline |
Java |
Pattern.DOTALL |
JavaScript |
不支持此模式 |
PHP |
/regex/s |
Python |
re.S re.DOTALL |
Ruby |
Regexp::MULTILINE /regex/m |
你可能注意到了,單行模式在不同語言中的稱呼很不一樣。比如在Java和Python中叫 DOTALL(也就是點號通配),這個名字確實更高明。不過,“單行模式”成了約定俗成的稱呼。
多行模式
單行模式影響的是點號的匹配規則:在默認模式下,點號 . 可以匹配除換行符之外的任何字符,在單行模式下,點號 . 可以匹配包括換行符在內的任何字符;多行模式影響的是^和$的匹配規則:在默認模式下,^和$匹配的是整個字符串的起始位置和結束位置,但在多行模式下,它們也能匹配字符串內部某一行文本的起始位置和結束位置。
假設,需要找到下面文本中所有數字字符開頭的行:
1 line
NO digit
2 line
解決這個問題,需要定位到每行的起始位置,嘗試匹配一個數字字符,如果成功,則匹配之后的整行文本。多行模式的模式修飾符是 m (Multiline),所以在表達式的開頭用(?m)指定多行模式,這樣^可以定位到字符串內部每一行的起始位置;匹配數字字符的表達式是 \d ,因為沒有指定單行模式,點號 . 不能匹配換行符,.*可以匹配“之后的整行文本”,整個表達式就是 (?m)^\d.* 。示例:
//多行模式(使用修飾符) String text1 = "1 line\r\nNo digit\r\n2 line"; Pattern p1 = Pattern.compile("(?m)^\\b\\d+\\b"); Matcher m1 = p1.matcher(text1); while(m1.find()){ System.out.println(m1.group()); }
//多行模式(使用參數) String text1 = "1 line\r\nNo digit\r\n2 line"; Pattern p1 = Pattern.compile("^\\b\\d+\\b",Pattern.MULTILINE); Matcher m1 = p1.matcher(text1); while(m1.find()){ System.out.println(m1.group()); }
下表列出常用語言中預定義常量的寫法:
語言 |
常量 |
.NET |
RegexOptions.Multiline |
Java |
Pattern.NULTILINE |
JavaScript |
/regex/m |
PHP |
/regex/m |
Python |
re.M re.MULTILINE |
Ruby |
默認即為多行模式 |
注釋模式
有時,用到的正則表達式可能非常復雜,不但難以編寫和閱讀,也難以維護。如果正則表達工也像程序源代碼一樣,可以添加注釋,閱讀和維護就容易多了。為了解決這個問題,許多語言支持使用 (?#comment)的記法添加注釋,comment就是注釋的內容。所以,這樣的表達式:^\d.*?$,就可以寫成這樣:
^(?#start of the line)\d(?#digit).*(?#sest of the line)
.NET,Python,Ruby,PHP都支持這種記法,Java與javascript則不支持。
來自:http://my.oschina.net/fhd/blog/370714