Java 正則表達式入門
j2se中一個重要的部分正則表達式很強大,它在處理字符串匹配時強大有余啊。主要涉及到java.util.regex包中的Pattern類與Matcher類。
下面從代碼中,走入正則表達式
代碼片段一,入門級的:
private static void testRegExpAccidence() {
//正則表達示第一個內容,點.可以替換任何一個字符。所以下面的應該是true。match(...)代碼,是否符合有三個字符.
sop("abc".matches("..."));
//正則表達示2,reaplaceAll第一參數支持正則,而\\d代表的是數字,就是說把所有數字替換成&。
sop("abc1234abc".replaceAll("\\d","&"));
//正則表達示3,compile的玩意。一般需要先把正則規則編譯成Pattern,而Pattern無構造方法,只能通過compile方法
Pattern pat = Pattern.compile("[abc]{3}");
Matcher mat = pat.matcher("abc"); //通過編譯出來的Pattern去匹配一個字符,返回的一個是匹配的結果用Matcher對象
sop(mat.matches());//其實也可以直接用"abc".matches("[abc]{3}") 來判斷字符串,是否符合[abc]{3}正則表達示
sop("------------. * + ?作用 --------------------");
//認為. * + ?的作用。
sop("a".matches(".")); //.匹配任意一個
sop("aaaa".matches("a*")); //X* 代表X出現0次或多次
sop("aaaa".matches("a+")); //X+ 代表X出現1次或多次
sop("aaaa".matches("a?"));//false //X? 代表X出現1次或0次
sop("".matches("a*"));
sop("".matches("a+")); //false
sop("".matches("a?"));
sop("a".matches("a*"));
sop("a".matches("a+"));
sop("a".matches("a?"));
sop("2343902594".matches("\\d{3,100}")); // \\d代表數字然后{3,100}代表至少出現3次,至多出現100次
sop("192.168.0.aaa".matches("\\d{3}\\.\\d{3}\\.\\d{3}\\.\\d{3}")); //false,其它未標識全部是true
sop("192".matches("[0-2][0-9][0-9]"));
sop("---------------范圍的匹配------------------------");
sop("a".matches("[abc]")); //true,[abc]代表,或者a或者b或者c,取三個之中的任意一個
sop("a".matches("[^abc]")); //false, [^abc]代表,不得是a或b或c,即不得是三個中的任意一個
sop("A".matches("[a-zA-Z]")); //true 范圍[a-zA-Z]代表是a-z或A-Z中的任意一個
sop("A".matches("[a-z]|[A-Z]"));//true,范圍[a-z]|[A-Z]也是代表a-z或A-Z中的任意一個
sop("A".matches("[a-z[A-Z]]"));//true,范圍[a-z[A-Z]]也是代表a-z或A-Z中的任意一個
sop("R".matches("[A-Z&&[RFG]]"));//true 范圍[A-Z&&[RFG]]代表取A-Z與RFG兩個范圍交集RFG中的任意一個
sop("---------------認識反斜杠s、反斜杠w、反斜杠d、反斜杠------------------------");
//\s \w \d \ \s代表空格 \w代表一個單詞(a-z0-9_A-Z) \d代表數字
// \ 通常和轉義一起用,如\t\r\n之類的,若只當成一個普通字符\,則需要再轉義一次 \\。
sop(" \n\r\t".matches("\\s{4}")); //\s,轉義需要再轉義一次即\\s,代表四個空格。
sop(" ".matches("\\S")); // \S,代表是\s的非取反,即不是空格(非空格)。 規則一般大寫是小寫的語義相反如\D是非數字 結果肯定false。
sop("a_8".matches("\\w{3}")); // \\w代表單詞中的字母,{3}代表出現三次。結果肯定是true了。
//sop("\\".matches("\\")); // 注意這里肯定會報錯,因為在matches正則中,先java解釋兩個\\是一個轉義,
// 但一個轉義讓正則去解釋肯定要報錯,它像java一樣要再用一個轉義來轉義它,證明它是一個普通的反斜杠。
sop("\\".matches("\\\\"));
sop("---------------認識boundary邊界 ^及$ \\b------------------------");
sop("hello sir".matches("^h.*")); //^不在中括號[]中,代表以什么開頭,如這里是代表以h開頭
sop("hello sir".matches(".*ir$")); //$代表以什么結尾,如這里是以ir結尾
// 這里有\\b代表 單詞邊界,可以以空格 \t或\r \n等來隔開。
sop("hello sir".matches("^h[a-z]{1,3}o\\b.*")); //這里的.要加上,代理單詞邊界后面還是字符,1個或多個。
sop("hellosir".matches("^h[a-z]{1,3}o\\b*")); //就是false了。
sop("---------幾個小練習------------");
sop("aaa 8888c".matches(".*\\d{4}.")); //肯定是true,因為.*代表任意出現0次或多次
sop("aaa 8888c".matches(".*\\b\\d{4}.")); //肯定是true,因為.*代表任意出現0次或多次
sop("aaa8888c".matches(".*\\d{4}."));
sop("aaa8888c".matches(".*\\b\\d{4}."));//這個就false了,因為\\b是單詞邊界,后面又跟著\d{4}肯定沒戲。
sop("chenssadf@aio7.com".matches("\\w+@\\w+\\.\\w+")); //這個雖然可以用,但似乎對前面的@單詞可以用.或-的沒有加上哦。
sop("-chenssadf@aio7.com".matches("[\\w.-]+@[\\w.-]+\\.\\w+"));
}
代碼片段二:掌握Matcher類的 find、start、end、lookingAt方法:
private static void testFindAndLookingAt() {
sop("---------------------matcher.find 與 matcher.lookingAt match.reset等方法測試--------------------------");
Pattern p = Pattern.compile("\\d{3,5}");
Matcher match = p.matcher("234-32132-4321-00");
sop(match.matches()); //matches方法與find方法相互操作是有影響的,因為兩者查看完會記錄出下個要操作的指針位置。
match.reset(); //比如這里沒有reset重置指針位置時,matches方法雖然匹配失敗false,但它吐進去的4個字符不會吐出來
sop(match.find()); //這樣,這里的第一個find從32132開始查了,就不從頭了。(除非前面有reset重置)。所以接下來最后兩上肯定是false了。
sop(match.start()+"--"+match.end()); //上面的find必須返回是true ,然后start是返回本次找到數據的第一位置,這里肯定是0,最后一個位置3(3是不包括)
sop(match.find());
sop(match.start()+"--"+match.end()); // start肯定就是4,end就是9了。
sop(match.find());
sop(match.start()+"--"+match.end());
sop(match.find());
//sop(match.start()+"--"+match.end()); //注意這里,不能打印的。因為上面的find返回的是false,根本沒有找到。所以start或end方法
//會報IllegalStateException: No match available
sop(match.lookingAt()); //這下面的lookingAt打印永遠都是true,因為lookingAt永遠每次都是重新重頭打開查。
sop(match.lookingAt());
}
代碼片段三:掌握強大的replace、replaceAll、appendReplacement方法
private static void testReplacement() {
sop("---------------------------match.replece及相關的替換測試---------------------");
//CASE_INSENSITIVE 大小寫不敏感即能吃呢 Pattern.CANON_EQ 默認的方法即是敏感必須相同
Pattern p = Pattern.compile("java",Pattern.CASE_INSENSITIVE);
//怎么把下面一串亂七八槽的java替換掉呢。比如只替換正則中規定的java
Matcher match = p.matcher("Java java JAva JAVa ILOVEJAVA youhateJaVa asdfasl;kdfj");
//sop(match.replaceAll("SOP")); //可以將java全部替換成SOP,或只把小寫java替換都可以。
//現有有個需求是,把單次數出現的java大小寫均可,替換成SOP,雙次數出現的替換成HELP。可以通過while循環find來搞
int count = 0;
StringBuffer buf = new StringBuffer(); //這里不可以寫StringBuilder,因為matcher后面的方法不支持Builder
while(match.find()){
if(0 == count++%2){
match.appendReplacement(buf, "HELP");
}else{
match.appendReplacement(buf, "SOP");
}
}
sop(match.appendTail(buf)); //把后面沒有find上的尾巴asdf等字符添加 進去。
}
代碼片段四:url中抓取郵件地址:
/**
* 通過向一網站,發送http請求,把請求回響回來的網頁內容進行解析,分析出來e-mail出來。
*/
private static void testUrlFetchEmail() throws Exception {
sop("-----------------用URL發送一個請求給網站,對返回的內部進行抓取e-mail地址出來----------------------");
//http://dzh.mop.com/whbm/20060220/3/S7gSlI706d198a73.shtml
URL url = new URL("http://dzh.mop.com/whbm/20060220/3/S7gSlI706d198a73.shtml");
sop(url.getContent());
sop(url.getQuery());
InputStream is = url.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
Pattern p = Pattern.compile("([\\w.-]+)@([\\w.-]+)\\.\\w+");
//Pattern p = Pattern.compile("^([\\w.-]+)@([\\w.-]+)\\.\\w+"); //這里把^加上去就掛了,代表這個字符串是以郵件開頭
String line = null; //從URL中讀出來的字符行,肯定不會是以郵件開頭的,至少都是<br>或<P>或<dir>之類的,不會以郵件開頭的吧。
while((line=br.readLine())!=null){
Matcher ma = p.matcher(line);
while(ma.find()){ //group必須與find一起來聯系。否則報:java.lang.IllegalStateException: No match found
sop(ma.group()+"這家伙用戶名:"+ma.group(1)+",用的公司域名:"+ma.group(2));
}
//sop(line);
//java.lang.IllegalStateException: No match found
}
}
代碼片段五:代碼行統計
/**
* 若是文件夾則遞歸,若是普通的.java文件則傳給統計代碼方法。
* @param dir 是傳入到此方法中 目錄的文件。如想查到D:\test文件夾(會以遞歸查所有文件)中所有代碼
* @throws Exception
*/
private static void parseDirCodeNum(File dir) throws Exception{
File[] files = dir.listFiles(new FilenameFilter(){
@Override
public boolean accept(File dir, String name) {
File tempFile = new File(dir,name);
if(tempFile.isFile()){ //若是個普通文件的話,則過濾一切不是.java的文件,文件夾為true,以便再次遞歸
if(name.endsWith(".java")){
return true ;
}else{
return false;
}
}
return true;
}
});
for(File f : files){
if(f.isDirectory()){
parseDirCodeNum(f);
}else{
parseFileDirNum(f); //是普通java文件則解析出代碼行數
}
}
}
/**
* 統計代碼行的方法。
* @param f
* @throws Exception
*/
private static void parseFileDirNum(File f) throws Exception{
BufferedReader br = new BufferedReader(new FileReader(f));
boolean isComment = false ;
String line = null ;
while((line = br.readLine())!=null){
line = line.trim(); //一開始就去掉取出來行中的 空格,因為對于注釋前面一般有\t影響stratsStart
if(line.startsWith("/*")&&line.endsWith("*/")){ //只有一行注釋
commentLine++;
}else if(line.startsWith("/*")){ //可能出現了多行注釋了,那么就標識注釋isComment開始了,true了。
commentLine++;
isComment = true;
}else if(isComment){ //要是標識開始了,未標識出false統統認為是注釋行。當然若注釋結束了*/了,則去掉標識。
commentLine++;
if(line.endsWith("*/")){ //這里沒有問題,因為注釋中,不能含有*/的。
isComment = false;
}
}else if(line.startsWith("http://")){
commentLine++;
}
else if(line.matches("^[\\s&&[^\\n]]*$")){ //空白行,必須是空白開頭且不能回車
whitespaceLine++;
}else {
normalLine++;
}
}
}
代碼片段六:掌握Greedy Reluctant Possessive數量詞的區別:
/**
* 測試Greedy Reluctant Possessive數量詞
* Greedy是貪吃 肯定是一上來,就以最多的吃掉,若不滿足且可退時,再退出一個再匹配...
* Reluctant是 勉強不情意的。 一上來,就吃掉最小的,若不滿足且可再吃時,就再吃一個匹配...
* Possessive是
*/
private static void testGreedyReluctantPossessive() {
sop("-------------測試 Greedy Reluctant Possessive數量詞-----------------------");
Pattern gp = Pattern.compile(".{3,10}[0-9]"); //Greedy方式
Pattern rp = Pattern.compile(".{3,10}?[0-9]"); //Reluctant方式 主要的與Greedy區別是多出一個?
Pattern pp = Pattern.compile(".{3,10}+[0-9]"); //Possessive方式 主要的與Greedy區別是多出一個+
Matcher gm = gp.matcher("asdf8sdfa9"); //通過Greedy匹配出來的匹配器
Matcher rm = rp.matcher("asdf8sdfa9"); //通過Reluctant匹配出來的匹配器
Matcher pm = pp.matcher("asdf8sdfa9"); //通過Possessive匹配出來的匹配器
if(gm.find()){ //因為gm是一次吃掉10個發現后面要求一個數字所以就退回了一個,這樣正好滿足找到了。
sop(gm.start()+" "+gm.end()); //返回0 10
}else{
sop("找不到匹配的");
}
if(rm.find()){ //因為rm是一次吃掉3發現后面要求一個數字,就再吃掉一個,這樣正好滿足找到了。
sop(rm.start()+" "+rm.end()); //返回0 5
}else{
sop("找不到匹配的");
}
if(pm.find()){ //因為pm是一次性吃掉10個,然后正規要求后面一個數字,但它不吐出來(看來更貪婪,難怪是獨占式的)。找不到了。
sop(pm.start()+" "+pm.end());
}else{
sop("找不到匹配的"); //返回找不到
}
}
代碼片段七相關測試代碼:
private static long normalLine ;
private static long commentLine ;
private static long whitespaceLine ;
public static void main(String[] args) throws Exception{
testRegExpAccidence();
testFindAndLookingAt();
testReplacement();
sop("-----------------match.group及相關的分組測試--------");
Pattern p = Pattern.compile("([\\w.-]+)@([\\w.-]+)\\.\\w+");
Matcher m = p.matcher("-chenshufei2@sina.com");
while(m.find()){
sop(m.group());
sop(m.group(1)); //可以得出用戶名-chenshufei2
sop(m.group(2)); //可以得出域公司名sina
//sop(m.group(3)); 會報出一個java.lang.IndexOutOfBoundsException: No group 3
//從此 可以驗證出,分組就是看正則中小括號(出現的位置,而上面的沒有第三個(,所以會報異常。
}
//testUrlFetchEmail(); //D:/java_setup_program_file/eclipse/design_pattern/EnhanceJava
String dirStr = "D:\\360Rec";
File dir = new File(dirStr);
parseDirCodeNum(dir);
sop("解析文件時,共有normalLine::"+normalLine+" ,commentLine::"+commentLine+" ,whitespaceLine::"+whitespaceLine);
testGreedyReluctantPossessive();
}
public static void sop(Object obj){
System.out.println(obj);
}
本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!