1 多行匹配
2 不以某某開(kāi)頭 ,比如不以www開(kāi)頭
3 不區分大小寫(xiě)
4 2個(gè)單元的或操作,比如 www | 3w 都可以這種
火龍果回答:
1:多行匹配
在默認的情況下 . 是不能匹配行結束符的(行結束符有 6 個(gè),具體的可以看看 Pattern 的 API DOC)
同樣,可以像不匹配大小寫(xiě)匹配那樣使用編譯參數:Pattern.DOTALL
如果還得區分大小寫(xiě)的話(huà),還得加上上面說(shuō)到的 Pattern.CASE_INSENSITIVE 這個(gè),舉個(gè)例子:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Test {
public static void main(String[] args) {
String str =
"<table> \n" +
" <tr> \n" +
" <td> \n" +
" Hello World! \n" +
" </td> \n" +
" </tr> \n" +
"</table>";
String regex = "<td>(.+?)</td>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while(matcher.find()) {
System.out.println(matcher.group(1).trim());
}
}
}
上面這個(gè)是不能從 str 抽取出東西的,因為 td 的后面帶有換行符,我們只要更改一下:
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
這樣就行了,如果 td 還得不區分大小寫(xiě)的話(huà),再改成:
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
這樣的話(huà),td 哪怕是大寫(xiě)的這個(gè)表達式都能把 td 之間的字符區抽取出來(lái)。
當然和 Pattern.CASE_INSENSITIVE 一樣,Pattern.DOTALL 也有內嵌標志表達式,即 (?s)
s 的意思表示 single-line 就是忽略換行符什么的,只看成單行進(jìn)行處理。
這個(gè)表達式使用內嵌 (?s) 的話(huà)可以改為:
String regex = "(?s)<td>(.+?)</td>";
如果還要不區分大小寫(xiě)的話(huà),再加上 i 標志:
String regex = "(?s)(?i)<td>(.+?)</td>";
但這樣顯得很拖沓,可以把它們合并起來(lái):
String regex = "(?is)<td>(.+?)</td>"; // 秩序無(wú)所謂
最后需要說(shuō)明一下的是,我曾看到過(guò)由于不明白 DOTALL,為了讓 . 匹配行結束符,直接把表達式寫(xiě)成:
String regex = "<td>((.|\\s)+?)</td>";
這樣做是極其危險的,由于選擇結構的匹配效率問(wèn)題,這樣做在比較長(cháng)的字符串時(shí)會(huì )造成堆棧溢出,
使程序崩潰,如果使用 DOTALL 或者 (?s) 的話(huà)就不會(huì )出現這種情況。
2:不以某某開(kāi)頭 ,比如不以www開(kāi)頭
public class Test {
public static void main(String[] args) {
String[] strs = {
"abc1232", "wwwadsf",
"awwwfas", "wwadfsf",
"", "ww", " ", "www"
};
String regex = "(?:(?!^www).)*";
for(String str : strs) {
System.out.printf("%-7s %s%n", str, str.matches(regex));
}
}
}
(?!X) 專(zhuān)業(yè)名稱(chēng)為 Negative Lookahead,表示字符間縫隙后面不允許出現的字符,
即匹配字符間的縫隙,如果縫隙后的字符不是 X 的話(huà),那這個(gè)縫隙就匹配成功。
舉個(gè)例子,aab 和 aac,現有表達式 aa(?!b) 這時(shí)我們能匹配到的字符串是 aac,
因為 aa 的后面的縫隙之后不允許出現字符 b,因此只有 aac 進(jìn)行了匹配。
再來(lái)看個(gè)示例:
public class Test {
public static void main(String[] args) {
String str = "AQuickBrownFoxJumpsOverTheLazyDog";
String[] strs = str.split("(?<!^)(?=[A-Z])");
for(String s : strs) {
System.out.println(s);
}
}
}
根據大寫(xiě)字母拆分字符串。當然了,這個(gè)使用字符串進(jìn)行分析同樣也能進(jìn)行拆分,
但是使用正則表達式來(lái)拆的話(huà)更為便捷直觀(guān)一些。
在進(jìn)行這種拆分時(shí),由于在拆分后的字符數不能減少,因此只能使用零寬度的
lookaround 功能進(jìn)行匹配,lookaround 包括四個(gè),即:
(?=X) (?!X) (?<=X) (?<!X)
來(lái)看一下這個(gè)表達式:(? <!^)(?=[A-Z])
前面說(shuō)到過(guò) (?!) 表示縫隙后面不允許出現的東西,而 (? <!) 表示縫隙前不允許出現的東西。
(?=) 表示縫隙后允許出現的東西,(? <=) 表示縫隙前允許出現的東西。
這個(gè)表達式在拆分時(shí),根據零寬度匹配縫隙進(jìn)行拆分的,這個(gè)縫隙必須滿(mǎn)足以下條件:
(? <!^) 表示縫隙不允許前不能是行開(kāi)始,即縫隙不能出現在首字母的前面。
(?=[A-Z]) 表示縫隙后面允許出現 A-Z 的大寫(xiě)字母。
這時(shí)這個(gè)表達式就匹配了下面帶有 | 的縫隙:
A|Quick|Brown|Fox|Jumps|Over|The|Lazy|Dog
PS:不加 (?<!^) 的話(huà),會(huì )變成:
|A|Quick|Brown|Fox|Jumps|Over|The|Lazy|Dog
根據 split 的功能,正則表達式處理程序就根據上面的 | 將字符串給拆分開(kāi)來(lái)了。
3,不區分大小寫(xiě)
不加任何限制的匹配是匹配分大小寫(xiě)的,但是正則表達式中可以進(jìn)行改變,
有兩種方式:參數式和內嵌式。
來(lái)看個(gè)示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Test {
public static void main(String[] args) {
String str = "Book";
Pattern pattern = Pattern.compile("book");
Matcher matcher = pattern.matcher(str);
System.out.println(matcher.matches());
}
}
上面的這個(gè)表達式 book 是不能匹配字符串 Book 的,這時(shí)我們只要給定編譯時(shí)的參數就可以了:
Pattern pattern = Pattern.compile("book", Pattern.CASE_INSENSITIVE);
Pattern.CASE_INSENSITIVE 這是一個(gè) int 類(lèi)型的常量,值為 2。表示表達式忽略大小寫(xiě)進(jìn)行區配。
如果我們不采用 Pattern 和 Matcher 兩個(gè)類(lèi)來(lái)匹配的話(huà),只是使用 String 的 matches 方法的話(huà),
我們就不能指定表達式的編譯參數了,這時(shí)就需要采用內嵌標志表達式了,與 Pattern.CASE_INSENSITIVE
對應的內嵌標志表達式是 (?i),它有四種形式:
1,(?i)
2,(?-i)
3,(?i:X)
4,(?-i:X)
不帶有 - 的是開(kāi)標志,帶有 - 的是關(guān)標志。
把上面的代碼改成這樣:
public class Test {
public static void main(String[] args) {
String str = "Book";
String regex = "(?i)book";
System.out.println(str.matches(regex));
}
}
我們就達到了同樣的效果,當然這樣并不是最好的,因為字符串中只有 B 是大寫(xiě)的,
我們沒(méi)有必要把所有的字符都進(jìn)行不區分大小寫(xiě)匹配,我們可以在打開(kāi)標志,用 (?i) 的
第二種形式馬上關(guān)掉它:
String regex = "(?i)b(?-i)ook";
這樣的話(huà),只有 b 是區分大小寫(xiě)了,而 (?-i) 后面的還是得區分大小寫(xiě)匹配的。這樣寫(xiě)
可能看上去很不順眼,我們還能使用第 3 種形式直接指定某些字符是不區分大小寫(xiě)的。
String regex = "(?i:b)ook";
這樣的表達式與上面的那個(gè)在語(yǔ)義上是相同的。就效率上肯定是優(yōu)于一下子開(kāi),一下子關(guān)的。
可見(jiàn)內嵌標志表達式要比指定編譯參數的功能強大許多。
使用建議:如果能確定某些字符的大小寫(xiě)時(shí),盡量使用已確定的字符,對于不確定的可以采用
(?i:X) 的方式指定。因此打開(kāi)不區分大小寫(xiě)開(kāi)關(guān)時(shí),對匹配的性能是有一定影響的。
思考一下:String regex = "(?i)b(?-i:oo)k"; 這個(gè)表達式的意思?
另外:第 1 和第 4,我沒(méi)看明白需要了解什么,請在下面的樓層中具體地說(shuō)明一下。
4:2個(gè)單元的或操作
| 稱(chēng)為多選結構,用于匹配 | 之中的任何一個(gè),拿你的例子來(lái)說(shuō)明:
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Test {
public static void main(String[] args) {
String str =
"<img src=\"http://www.google.com/1.gif\"/>\n" +
"<img src=\"http://3w.google.com/1.gif\"/>\n" +
"<img src=\"http://abc.baidu.com/1.gif\"/>";
String regex = "<img\\ssrc=\"http://(?:ww|3)w.google.com/1.gif\"/>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while(matcher.find()) {
System.out.println(matcher.group());
}
}
}
注意到其中的 (?:ww |3) 在進(jìn)行多選匹配時(shí)盡量找出多選中的規律,以減少多選的字符,
www 和 3w 在最后一個(gè)字符可以共用,前面的不一樣。
(?: ) 的意思表示組成一組,如果沒(méi)有 (?: ) 這樣的話(huà),表達式就變成了:
String regex = "<img\\ssrc=\"http://ww|3w.google.com/1.gif\"/>";
這樣的語(yǔ)義完全變掉了, | 是在一組中進(jìn)行選擇,由于上面的那個(gè)表達式中沒(méi)有組,就把整個(gè)表
達式作為了一組,使用 | 的話(huà),就進(jìn)行了整個(gè)表達式的多選結構了。這個(gè)表達式的意思是:
匹配 <img ssrc="http://ww 或者是 3w.google.com/1.gif"/>,這樣的結果并不是我們所要的。
我們僅僅需要在 ww 和 3 之間進(jìn)行選擇,這時(shí)只要把 ww 和 3 放在一組中進(jìn)行多選擇就可以了,
變成 (?:ww |3)。
還有,在多選結構中盡量把出現頻率高的放在前面,這樣可以加快匹配速度。
多選結構的效率在傳統型的引擎中是效率低下的,如果是單個(gè)字符的選擇,比如 a $ & 之中的一個(gè),
那就不要使用 (?:a |$ |&) 了,可以直接使用字符類(lèi) [a$&] 就可以了。