Java 正則表達式入門

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