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 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!