正则表达式小记

作者: 数齐 | 来源:发表于2018-03-24 10:06 被阅读66次

    正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。现阶段很多语言有正则的实现,比如java,python等等。简单的关于字符串的替换查找等的操作可以通过字符串的API来操作,但是有些情况,通过API要进行很多额外的操作才能完成的功能,通过正则一个简单的表达式就可以轻松搞定,不信请往下看。

    基础语法

    一. 表示位置

    1. ^ 匹配输入字符串的开始位置。$ 匹配输入字符串的结束位置

    @Test
    public void test15() {
        Pattern pattern = Pattern.compile("^a.*b$");
        Matcher matcher1 = pattern.matcher("adb");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("db");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("ad");
        System.out.println(matcher3.find());  //true
    }
    } 
    

    二. 表示次数

    3. * 匹配前面的子表达式零次或多次,相当于{0,}

    @Test
    public void test16(){
        Pattern pattern = Pattern.compile("a*");
        Matcher matcher1 = pattern.matcher("");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("a");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("aa");
        System.out.println(matcher3.find());  //true
    }
    

    4. + 匹配前面的子表达式一次或多次,相当于{1,}

    @Test
    public void test17(){
        Pattern pattern = Pattern.compile("a+");
        Matcher matcher1 = pattern.matcher("");
        System.out.println(matcher1.find());  //false
    
        Matcher matcher2 = pattern.matcher("a");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("aa");
        System.out.println(matcher3.find());  //true
    }
    

    5. ? 匹配前面的子表达式零次或一次,相当于{0,1}

    @Test
    public void test18(){
        Pattern pattern = Pattern.compile("do(es)?");
        Matcher matcher1 = pattern.matcher("do");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("does");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("doe");
        System.out.println(matcher3.find());  //true
    }
    

    0次或者1次,那么也就是do或者does,所以包含do或者does的都会被选出来,doe也包含do,所以也查找到了。

    6. {n} n是一个非负整数。匹配确定的n次

    @Test
    public void test19(){
        Pattern pattern = Pattern.compile("n{2}");
        Matcher matcher1 = pattern.matcher("abnn");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("abn");
        System.out.println(matcher2.find());  //false
    
        Matcher matcher3 = pattern.matcher("ab");
        System.out.println(matcher3.find());  //false
    }
    

    7. {n,} n是一个非负整数。至少匹配n次

    @Test
    public void test20(){
        Pattern pattern = Pattern.compile("n{2,}");
        Matcher matcher1 = pattern.matcher("abnnn");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("abnn");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("abn");
        System.out.println(matcher3.find());  //false
    }
    

    8. {n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次

    @Test
    public void test21() {
        System.out.println("abnnnn".replaceAll("n{2,3}", ""));  //abn 替换了前面3个n为空的字符串
        System.out.println("abnnn".replaceAll("n{2,3}", ""));  //ab 替换了3个n为空的字符串
        System.out.println("abnn".replaceAll("n{2,3}", ""));  //ab 替换了2个n为空的字符串
        System.out.println("abn".replaceAll("n{2,3}", ""));  //abn 未匹配到,不做替换,还是原来字符创
    }
    

    9. 特殊的?:当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串

    @Test
    public void test22() {
        Pattern pattern = Pattern.compile("n{2,3}?"); //贪婪模式
        Matcher matcher1 = pattern.matcher("abnnnnn");
        while (matcher1.find()){
           System.out.println(matcher1.group());
        }
    }
    

    结果:nn输出两遍。因为是懒惰模式,所以会找最小满足要求的,也就是连续的两个n,因为我们现在有连续5个n,所以可以找到2次

    三.表示功能(实在找不到别的词了)

    10. · 匹配除换行符(\n)与回车符(\r)之外的任何单个字符。

        @Test
    public void test23(){
        Pattern pattern = Pattern.compile(".");
        Matcher matcher1 = pattern.matcher("a");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("3");
        System.out.println(matcher2.find());  //true
    
    
        Matcher matcher3 = pattern.matcher("\t");
        System.out.println(matcher3.find());  //true
    
        Matcher matcher4 = pattern.matcher("\r");
        System.out.println(matcher4.find());  //false
    
        Matcher matcher5 = pattern.matcher("\n");
        System.out.println(matcher5.find());  //false
    
    
    }
    

    11. \d 匹配一个数字字符,等价于 [0-9]。\D 匹配一个非数字字符。等价于 [^0-9]

    @Test
    public void test24(){
        Pattern pattern = Pattern.compile("^\\d+$");
        Matcher matcher1 = pattern.matcher("23");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("3d");
        System.out.println(matcher2.find());  //false
    
    
        Pattern pattern2 = Pattern.compile("^\\D+$");
        Matcher matcher3 = pattern2.matcher("23");
        System.out.println(matcher3.find());  //false
    
        Matcher matcher4 = pattern2.matcher("3d");
        System.out.println(matcher4.find());  //false
    
        Matcher matcher5 = pattern2.matcher("dd");
        System.out.println(matcher5.find());  //true
        
    }
    

    12. \w 匹配字母、数字、下划线,等价于'[A-Za-z0-9_]'。\W匹配非字母、数字、下划线,等价于 '[^A-Za-z0-9_]'。

        @Test
    public void test25(){
        Pattern pattern = Pattern.compile("^\\w+$");
        Matcher matcher1 = pattern.matcher("3Dd_");
        System.out.println(matcher1.find());  //true
        
        Pattern pattern2 = Pattern.compile("^\\W+$");
        Matcher matcher3 = pattern2.matcher("\t\n\r\b");
        System.out.println(matcher3.find());  //true
    
    }
    

    13. \s 匹配任何空白字符,包括空格、制表符、换页符等等。\S匹配任何非空白字符。

    @Test
    public void test26(){
        Pattern pattern = Pattern.compile("^\\s+$");
        Matcher matcher1 = pattern.matcher(" \t\r\n");
        System.out.println(matcher1.find());  //true
    
        Pattern pattern2 = Pattern.compile("^\\S+$");
        Matcher matcher3 = pattern2.matcher("aA3_");
        System.out.println(matcher3.find());  //true
    
    }
    

    四. 范围中匹配一个

    14. x|y 匹配x或y。

    @Test
    public void test27(){
        Pattern pattern = Pattern.compile("a|b");
        Matcher matcher1 = pattern.matcher("ac");
        System.out.println(matcher1.find());  //true
    
        Matcher matcher2 = pattern.matcher("bc");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("cc");
        System.out.println(matcher3.find());  //false
    
    
    }
    

    15. [xyz] 匹配所包含的任意一个字符。[^xyz]匹配未包含的任意字符。

    @Test
    public void test29(){
        Pattern pattern = Pattern.compile("^[abc]+$");
        Matcher matcher1 = pattern.matcher("aA");
        System.out.println(matcher1.find());  //false
    
        Matcher matcher2 = pattern.matcher("bb");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("CC");
        System.out.println(matcher3.find());  //false
    
        Pattern pattern2 = Pattern.compile("^[^abc]+$");
        Matcher matcher4 = pattern2.matcher("aA");
        System.out.println(matcher4.find());  //false
    
        Matcher matcher5 = pattern2.matcher("bb");
        System.out.println(matcher5.find());  //false
    
        Matcher matcher6 = pattern2.matcher("CC");
        System.out.println(matcher6.find());  //true
    
    }
    

    16. [a-z] 匹配指定范围内的任意字符。[^a-z]匹配任何不在指定范围内的任意字符。

     @Test
    public void test28(){
        Pattern pattern = Pattern.compile("^[a-z]+$");
        Matcher matcher1 = pattern.matcher("aA");
        System.out.println(matcher1.find());  //false
    
        Matcher matcher2 = pattern.matcher("bb");
        System.out.println(matcher2.find());  //true
    
        Matcher matcher3 = pattern.matcher("CC");
        System.out.println(matcher3.find());  //false
    
        Pattern pattern2 = Pattern.compile("^[^a-z]+$");
        Matcher matcher4 = pattern2.matcher("aA");
        System.out.println(matcher4.find());  //false
    
        Matcher matcher5 = pattern2.matcher("bb");
        System.out.println(matcher5.find());  //false
    
        Matcher matcher6 = pattern2.matcher("CC");
        System.out.println(matcher6.find());  //true
    
    }
    

    五. 捕获组

    捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用。捕获组有两种形式,一种是普通捕获组,另一种是命名捕获组,通常所说的捕获组指的是普通捕获组

    17. 普通捕获组 如果没有显式为捕获组命名,即没有使用命名捕获组,那么需要按数字顺序来访问所有捕获组。在只有普通捕获组的情况下,捕获组的编号是按照“(”出现的顺序,从左到右,从1开始进行编号的 。编号为0的捕获组,指的是正则表达式整体,这一规则在支持捕获组的语言中,基本上都是适用的

        @Test
    public void test12() {
        String pat = "\\S*(\\d{4})-(\\d{2}-(\\d{2}))\\S*";
        Pattern pattern = Pattern.compile(pat);
        Matcher matcher = pattern.matcher("今天是2018-12-13,我好开心");
        System.out.println(matcher.find());  //true
        System.out.println(matcher.group(0));  //今天是2018-12-13,我好开心
        System.out.println(matcher.group(1));  //2018
        System.out.println(matcher.group(2));  //12-13
        System.out.println(matcher.group(3));  //13
    }
    

    18. 命名捕获组 命名捕获组通过显式命名,可以通过组名方便的访问到指定的组,而不需要去一个个的数编号,同时避免了在正则表达式扩展过程中,捕获组的增加或减少对引用结果导致的不可控。不过容易忽略的是,命名捕获组也参与了编号的,在只有命名捕获组的情况下,捕获组的编号也是按照“(”出现的顺序,从左到右,从1开始进行编号的 。

    @Test
    public void test13() {
        String pat = "\\S*(?<year>\\d{4})-(?<date>\\d{2}-(?<day>\\d{2}))\\S*";
        Pattern pattern = Pattern.compile(pat);
        Matcher matcher = pattern.matcher("今天是2018-12-13,我好开心");
        System.out.println(matcher.find());  //true
        System.out.println(matcher.group(0));  //今天是2018-12-13,我好开心
    
    
        System.out.println(matcher.group(1));  //2018
        System.out.println(matcher.group("year"));  //2018
    
        System.out.println(matcher.group(2));  //12-13
          System.out.println(matcher.group("date"));  12-13
    
        System.out.println(matcher.group(3));  //13
        System.out.println(matcher.group("day"));  //13
    
    }
    

    上面所说的是是获取匹配,下面我们说费获取匹配。正向表示匹配前面,反向表示匹配后面。又叫零宽断言

    19. (?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用

       @Test
    public void test10() {
        String str = "industry|industries";
        String str1 = str.replaceAll("(industr(?:y|ies))", "");
        System.out.println(str1); //  | 说明都匹配到了
        String str2 = str.replaceAll("(industr[y|ies])", "");
        System.out.println(str2); //  |es 说明都匹配到了
    }
    

    20. (?=pattern) 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。匹配pattern前面的位置

     @Test
    public void test1() {
        String str = "windows95,windows100";
        //正向肯定预查,匹配windows95中的windows,或者windows1000中的windows
        str = str.replaceAll("(windows(?=95|1000))", "");
        System.out.println(str); //95,windows100
    }
    

    21. (?!pattern) 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。匹配pattern后面的位置

       @Test
    public void test2() {
        String str = "swindows95,rwindows100";
        //正向否定预查,不匹配windows95中的windows,或者不匹配windows1000中的windows
        str = str.replaceAll("(windows(?!95|1000))", "");
        System.out.println(str); //swindows95,r100
    }
    

    22. (?<=pattern) 反向肯定预查,与正向肯定预查类似,只是方向相反。匹配后面跟的不是pattern的位置

      @Test
    public void test3() {
        String str = "95windows,100windows";
        //反向肯定预查,匹配95windows中的windows或者是1000windows中的windows
        str = str.replaceAll("(?<=95|1000)windows", "");
        System.out.println(str); //95,100windows
    }
    

    23. (?<!pattern) 反向否定预查,与正向否定预查类似,只是方向相反。匹配前面不是pattern的位置

    @Test
    public void test4() {
        String str = "95windows,100windows";
        //反向否定预查,不匹配95windows中的windows和1000windows中的windows
        str = str.replaceAll("(?<!95|1000)windows", "");
        System.out.println(str); //95windows,100
    }
    

    实战

    1.压缩xml

    我们发送的或者接收到的xml是有格式的。

    <?xml version="1.0" encoding="GBK" standalone="yes"?>
    <user>
        <header>
            <name>linyang</name>
            <age>26</age>
        </header>
        <body>
            <address>地址</address>
            <postcode>234321</postcode>
        </body>
    </user>
    

    我们需要将标签之间的空白字符都去掉,完成我们的压缩目的,一句代码搞定。

    @Test
    public void test40(){
        String str="<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?>\n" +
                "\<user>\n" +
                "    \<header>\n" +
                "        \<name>shuqi\</name>\n" +
                "        \<age>26\</age>\n" +
                "    \</header>\n" +
                "    \<body>\n" +
                "        \<address>地址\</address>\n" +
                "        \<postcode>234321\</postcode>\n" +
                "    \</body>\n" +
                "\</user>";
        str = str.replaceAll("(?<=>)\\s+(?=<)", "");
        System.out.println(str);
    }
    

    结果是:

    <?xml version="1.0" encoding="GBK" standalone="yes"?><user><header><name>shuqi</name><age>26</age></header><body><address>地址</address><postcode>234321</postcode></body></user>
    

    很好的完成了压缩的目的,利用的就是零宽断言。

    本文收录的不是全部的正则表达式,只是相对来说比较常用的,他再文本匹配、搜索、替换都有较高的应用,掌握它我们会事半功倍。好玩的示例补充中,未完待续,敬请期待。

    参考文档

    正则表达式30分钟入门教程

    正则基础之——捕获组(capture group)

    表达式全集

    相关文章

      网友评论

      本文标题:正则表达式小记

      本文链接:https://www.haomeiwen.com/subject/iafqcftx.html