Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 335|回复: 0

[正则表达式学习]牛人讲解正则表达式强悍版

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-28 23:58:42 | 显示全部楼层 |阅读模式
    题目,请解释一下两个正则表达式所代表的含义:
           // 限定条件
           final String CONDITION = "(?=.*[a-z])(?=.*[A-Z])(?=.*\d)";  
           // 允许出现的字符  
           final String SPECIAL_CHAR = "[-A-Za-z0-9!$%&()/;<?{}\[\]^\\]";
      
           牛人“bao110908”解答:
           1、表达式1
           (?=.*[a-z]) 表示当前位置后面必须出现 .*[a-z] 的字符,这个可以理解为必须出现小写字母。或者可以理解为某一个字符间的缝隙必须满足的条件,这个仅仅作为条件判断并不能匹配任何字符,因为这属于非捕获 组中的环视(lookarround)零宽度匹配。

    举个大家常见的例子:

    表达式:Win(?=XP)
    现有字符串 WinXP 和 WinNT,在应用这个表达式时前者能与之进行匹配,为什么呢?

    当匹配器指示到 (?=XP) 时,也就是在 n 字母后面的缝隙,这个缝隙必须满足的条件是:后面的字符必须是 XP,如果是的话,匹配成功,否则匹配失败。由于(?=XP) 是匹配缝隙的,因此并不会把 XP 给匹配输出,而只输出了 Win 因此,这个表达式的语义可以看作是:找到后面为“XP”字符所有的 Win。

    假如,我们把表达式写成 Win(?=XP)(?=NT) 这样的话,那么这个语义是:找出后面为“XP”并且为“NT”字符所有的 Win 可以想象,这是个永远无法满足的匹配。(?=XP)(?=NT) 这个表示当前的缝隙必须同时满足的条件。

    把这个表达式再改一下,改成 Win(?=.*XP)(?=.*NT) 这个表示 Win 的后面必须出现XP 与 NT,位置和顺序都是无关的(这主要是 .* 的作用)。当然了这个表达式的效率是比较低的,得向后进行两次断言。

    如果字符串是 WincbaXPabcNT 这个字符串,当匹配指示器走到 n 后面的缝隙时开始进行向后断言,首先对 .*XP 进行断言,很明显能将 cbaXP 匹配成功,这时第一个断言完成,再对 .*NT 断言,可以看出 cbaXPabcNT 能与其匹配成功,这时第二个断言完成,因此表达式 Win(?=.*XP)(?=.*NT) 能对 WincbaXPabcNT 进行匹配。

    同理 WincbaNTabcXP 也是同样的效果。
    如果能理解上面的这些,对于 (?=.*[a-z])(?=.*[A-Z])(?=.*\d) 这个的理应该不会很难吧,这个只不过是必须同时满足三个条件。

    这个表达式在开始部分就进行断言,即索引为 0 的地方,也就是第一个字符的前面的缝隙,这个缝隙后面的字符必须满足 .*[a-z] .*[A-Z] .*\d 三个条件,也就是说必后面必须出现至少一个小写字母、至少一个大写母、至少一个数字。
      
           2、表达式2
           至于表达式 2 的使用,也就是 [ ] 内字符的转义需要注意一下。

    ^ 和 - 在 [ ] 结构的表达式中是有一定意义的。

    [^abc] 表示除 abc 以外所有的字符,注意,这是放在最前面表示这个意思,如果改成 [a^bc] 这个仅表示 a ^ b c 四个字符。如果需要匹配 ^ 这个字符的话,千万不要把它放在第一个,如果一定要放在第一个的话,得使用转义符。

    - 在 [ ] 表示字符的范围,比如 [a-z] 表示 a 与 z 之间的 26 个字母,[a-zA-Z] 这个表示 a-z 和 A-Z 的 52 个字母。使用范围得注意了,如果写成[z-a] 的话,在 Pattern.compile 编译表达式时会对范围进行检查,这时会产生异常,因此在使用 - 范围时,后面的 Unicode 值必须大于等于前面的 Unicode值。

    如果要匹配“-”的话,尽量不要把 - 这个放在字符之间,可以放在 [ ] 的两边。比如 [-a-z] 这就能匹配 26 个小写字母和“-”了。当然了,我们也可以写成[a-z-A-Z] 这个可以匹配 52 字母和“-”,但是这样很不直观,我们宁愿写成[a-zA-Z-] 或者 [-a-zA-Z] 这样。

    继续整理牛人的正则表达式解答!!!
           1、不以某某开头 ,比如不以www开头
            
             
             
              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));   
                      }   
                  }   
              }   
             
             
            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));
            }
        }
    }[/code] (?!X) 专业名称为 Negative Lookahead,表示字符间缝隙后面不允许出现的字符,
            
    即匹配字符间的缝隙,如果缝隙后的字符不是 X 的话,那这个缝隙就匹配成功。
            

            
    举个例子,aab 和 aac,现有表达式 aa(?!b) 这时我们能匹配到的字符串是 aac,
            
    因为 aa 的后面的缝隙之后不允许出现字符 b,因此只有 aac 进行了匹配。
            

            
    再来看个示例:
            

             
             
              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);   
                      }   
                  }   
              }   
             
             
            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);
            }
        }
    }[/code] 根据大写字母拆分字符串。当然了,这个使用字符串进行分析同样也能进行拆分,
            
    但是使用正则表达式来拆的话更为便捷直观一些。
            

            
    在进行这种拆分时,由于在拆分后的字符数不能减少,因此只能使用零宽度的
            
    lookaround 功能进行匹配,lookaround 包括四个,即:
            
            
             
             
              (?=X)  (?!X)  (?<=X)  (?<!X)   
             
             
            (?=X)  (?!X)  (?<=X)  (?<!X)[/code] 来看一下这个表达式:(?<!^)(?=[A-Z])
            

            
    前面说到过 (?!) 表示缝隙后面不允许出现的东西,而 (?<!) 表示缝隙前不允许出现的东西。(?=) 表示缝隙后允许出现的东西,(?<=) 表示缝隙前允许出现的东西。
            

            
    这个表达式在拆分时,根据零宽度匹配缝隙进行拆分的,这个缝隙必须满足以下条件:
            
    (?<!^) 表示缝隙不允许前不能是行开始,即缝隙不能出现在首字母的前面。
            
    (?=[A-Z]) 表示缝隙后面允许出现 A-Z 的大写字母。
            
    这时这个表达式就匹配了下面带有 | 的缝隙:
            

             
             
              A|Quick|Brown|Fox|Jumps|Over|The|Lazy|Dog   
              PS:不加 (?<!^) 的话,会变成:   
              |A|Quick|Brown|Fox|Jumps|Over|The|Lazy|Dog   
             
             
            A|Quick|Brown|Fox|Jumps|Over|The|Lazy|Dog
    PS:不加 (?<!^) 的话,会变成:
    |A|Quick|Brown|Fox|Jumps|Over|The|Lazy|Dog[/code] 根据 split 的功能,正则表达式处理程序就根据上面的 | 将字符串给拆分开来了。
            

            2、不区分大小写
            
          
            不加任何限制的匹配是匹配分大小写的,但是正则表达式中可以进行改变,
            
    有两种方式:参数式和内嵌式。
            

            
    来看个示例:
            

            
            
             
             
              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());   
                  }   
              }   
             
             
            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());
        }
    }[/code] 上面的这个表达式 book 是不能匹配字符串 Book 的,这时我们只要给定编译时的参数就可以了:
            

            
    Pattern pattern = Pattern.compile("book", Pattern.CASE_INSENSITIVE);
            
    Pattern.CASE_INSENSITIVE 这是一个 int 类型的常量,值为 2。表示表达式忽略大小写进行区配。
            

            
    如果我们不采用 Pattern 和 Matcher 两个类来匹配的话,只是使用 String 的 matches 方法的话,我们就不能指定表达式的编译参数了,这时就需要采用内嵌标志表达式了,与 Pattern.CASE_INSENSITIVE
            
    对应的内嵌标志表达式是 (?i),它有四种形式:
            
    1,(?i)
            
    2,(?-i)
            
    3,(?i:X)
            
    4,(?-i:X)
            
    不带有 - 的是开标志,带有 - 的是关标志。
            
    把上面的代码改成这样:
            

             
             
              public class Test {   
                
                  public static void main(String[] args) {           
                      String str = "Book";   
                      String regex = "(?i)book";   
                      System.out.println(str.matches(regex));   
                  }   
              }   
             
             
            public class Test {
        public static void main(String[] args) {        
            String str = "Book";
            String regex = "(?i)book";
            System.out.println(str.matches(regex));
        }
    }[/code] 我们就达到了同样的效果,当然这样并不是最好的,因为字符串中只有 B 是大写的,
            
    我们没有必要把所有的字符都进行不区分大小写匹配,我们可以在打开标志,用 (?i) 的
            
    第二种形式马上关掉它:
            
       String regex = "(?i)b(?-i)ook";
            

            
    这样的话,只有 b 是区分大小写了,而 (?-i) 后面的还是得区分大小写匹配的。这样写
            
    可能看上去很不顺眼,我们还能使用第 3 种形式直接指定某些字符是不区分大小写的。
            
       String regex = "(?i:b)ook";
            

            
    这样的表达式与上面的那个在语义上是相同的。就效率上肯定是优于一下子开,一下子关的。
            

            
    可见内嵌标志表达式要比指定编译参数的功能强大许多。
            

            
    使用建议:如果能确定某些字符的大小写时,尽量使用已确定的字符,对于不确定的可以采用
            
    (?i:X) 的方式指定。因此打开不区分大小写开关时,对匹配的性能是有一定影响的。
            

            
    思考一下:String regex = "(?i)b(?-i:oo)k"; 这个表达式的意思?
            

            

            
           3、多行匹配
          
            在默认的情况下 . 是不能匹配行结束符的(行结束符有 6 个,具体的可以看看 Pattern 的 API DOC)
            
    同样,可以像不匹配大小写匹配那样使用编译参数:Pattern.DOTALL
            

            
    如果还得区分大小写的话,还得加上上面说到的 Pattern.CASE_INSENSITIVE 这个,举个例子:
            

             
             
              import java.util.regex.Matcher;   
              import java.util.regex.Pattern;   
                
              public class Test {   
                
                  public static void main(String[] args) {   
                      String str =   
                              "<table>               
    " +   
                              "  <tr>                 
    " +   
                              "    <td>               
    " +   
                              "       Hello World!   
    " +   
                              "    </td>              
    " +   
                              "  </tr>               
    " +   
                              "</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());   
                      }           
                  }   
              }   
             
             
            import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    public class Test {
        public static void main(String[] args) {
            String str =
                    "<table>               
    " +
                    "  <tr>                 
    " +
                    "    <td>               
    " +
                    "       Hello World!   
    " +
                    "    </td>              
    " +
                    "  </tr>               
    " +
                    "</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());
            }        
        }
    }
    [/code] 上面这个是不能从 str 抽取出东西的,因为 td 的后面带有换行符,我们只要更改一下:
            

            
    Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
            

            
    这样就行了,如果 td 还得不区分大小写的话,再改成:
            

             
             
              Pattern pattern = Pattern.compile(regex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);   
             
             
            Pattern pattern = Pattern.compile(regex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);[/code] 这样的话,td 哪怕是大写的这个表达式都能把 td 之间的字符区抽取出来。
            

            
    当然和 Pattern.CASE_INSENSITIVE 一样,Pattern.DOTALL 也有内嵌标志表达式,即 (?s) 的意思表示 single-line 就是忽略换行符什么的,只看成单行进行处理。
            

            
    这个表达式使用内嵌 (?s) 的话可以改为:
            

             
             
              String regex = "(?s)<td>(.+?)</td>";   
                
              如果还要不区分大小写的话,再加上 i 标志:   
              String regex = "(?s)(?i)<td>(.+?)</td>";   
                
              但这样显得很拖沓,可以把它们合并起来:   
              String regex = "(?is)<td>(.+?)</td>";    // 秩序无所谓   
             
             
            String regex = "(?s)<td>(.+?)</td>";
    如果还要不区分大小写的话,再加上 i 标志:
    String regex = "(?s)(?i)<td>(.+?)</td>";
    但这样显得很拖沓,可以把它们合并起来:
    String regex = "(?is)<td>(.+?)</td>";    // 秩序无所谓[/code] 最后需要说明一下的是,我曾看到过由于不明白 DOTALL,为了让 . 匹配行结束符,直接把表达式写成:
            

             
             
              String regex = "<td>((.|\s)+?)</td>";   
             
             
            String regex = "<td>((.|\s)+?)</td>";[/code] 这样做是极其危险的,由于选择结构的匹配效率问题,这样做在比较长的字符串时会造成堆栈溢出,
            
    使程序崩溃,如果使用 DOTALL 或者 (?s) 的话就不会出现这种情况。
            
          
            

            
            
            4、2个单元的或操作 | 称为多选结构,用于匹配 | 之中的任何一个,拿你的例子来说明:
            

            
            
             
             
              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"/>
    " +   
                              "<img src="http://3w.google.com/1.gif"/>
    " +   
                              "<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());   
                      }           
                  }   
              }   
             
             
            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"/>
    " +
                    "<img src="http://3w.google.com/1.gif"/>
    " +
                    "<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());
            }        
        }
    }[/code] 注意到其中的 (?:ww|3) 在进行多选匹配时尽量找出多选中的规律,以减少多选的字符,
            
    www 和 3w 在最后一个字符可以共用,前面的不一样。
            

            
    (?: ) 的意思表示组成一组,如果没有 (?: ) 这样的话,表达式就变成了:
            

             
             
              String regex = "<img\ssrc="http://ww|3w.google.com/1.gif"/>";   
             
             
            String regex = "<img\ssrc="http://ww|3w.google.com/1.gif"/>";[/code] 这样的语义完全变掉了,| 是在一组中进行选择,由于上面的那个表达式中没有组,就把整个表达式作为了一组,使用 | 的话,就进行了整个表达式的多选结构了。这个表达式的意思是:
            
    匹配 <img ssrc="http://ww 或者是 3w.google.com/1.gif"/>,这样的结果并不是我们所要的。
            

            
    我们仅仅需要在 ww 和 3 之间进行选择,这时只要把 ww 和 3 放在一组中进行多选择就可以了,变成 (?:ww|3)。
            

            
    还有,在多选结构中尽量把出现频率高的放在前面,这样可以加快匹配速度。
            

            
    多选结构的效率在传统型的引擎中是效率低下的,如果是单个字符的选择,比如 a $ & 之中的一个,那就不要使用 (?:a|$|&) 了,可以直接使用字符类 [a$&] 就可以了。
            

            

            
          
            参考地址:http://topic.csdn.net/u/20080627/14/8a91b33a-f35c-4303-85b5-e0a8da462202.HTML
            

            
          
            

            
          
          
          
          
         
       

       
         
         
          
          

            
          

            
          
         
       

      


    源码下载:http://file.javaxxz.com/2014/10/28/235841750.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-26 01:34 , Processed in 0.301326 second(s), 36 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表