正则表达式

正则表达式(regular expression)

视频传送门

快速入门

image

public class Regexp {
    public static void main(String[] args) {
        String str = "返回我ewr爱俄rte方叫我金佛trei哇56iw二分ui哦t为覅ewr覅欧56文哈i欧文覅后卫阿红i娃儿忽然覅欧文";
//        getLetter(str);
        System.out.println("------------");
        String str2 = "<a target=\"_blank\" title=\"标题1\">";
//        getLetter(str2);
        System.out.println("------------");
        String str3 = "192.168.23.12 192.168.255.255";
        getLetter(str3);

    }

    //提取文章中所有的英文单词
    public static void getLetter(String str) {
        /*
         * 传统方法,(变量方式)每一个字符去取出来来匹配ascll表,代码量大,效率不高
         * 正则表达式技术
         * */
        //1. 先创建 Pattern对象,模式对象,可以理解为正则表达对象
        //Pattern pattern = Pattern.compile("[a-zA-Z]+"); //匹配字母
//        Pattern pattern = Pattern.compile("[0-9]|[a-zA-Z]+"); // 匹配数字字母
//        Pattern pattern = Pattern.compile("<a target=\"_blank\" title=\"(\\S*)\""); //提取百度热搜的标题
        Pattern pattern = Pattern.compile("(\\d+\\.\\d+\\.\\d+\\.\\d+)"); //提取ip 地址

        //2. 创建一个匹配器对象
        //理解 匹配器 按照 pattern(模式/样式),到 str文本中去匹配
        //找到就返回 true,否则返回false
        Matcher matcher = pattern.matcher(str);
        //3. 开始循环匹配
        while (matcher.find()) {
            //匹配到的内容,文本,放到 m.group(0)
            System.out.println("找到:" + matcher.group(0));// 0 获得匹配内容
//            System.out.println("找到:" + matcher.group(1));// 1 获得分组,括号内容
        }
    }
}

再提出几个问题

image

image

解决之道-正则表达式

  • 为了结局上述问题,Java提供了正则表达式技术,专门用户处理类似文本处理问题。
  • 简单的说:正则表达式是对字符串执行模式匹配的技术
  • 正则表达式:regular expression =》RegExp

正则表达式基本介绍

  • 一个正则表达式,就是用某种模式去匹配字符串的一个公式。很多人因为他们看上去比较古怪而且复杂所以不敢去使用,不过经过联系后,就觉得这些复杂的表达式写起来还是相当简单的,而且,一旦你弄懂他们,你就能把数小时辛苦热切易错的文笔处理工作缩短在几分钟(甚至几秒中)内完成。
  • 正则表达式不只是只有 java才有,实际上很多编程语言都支持正则表达式进行字符串操作。

正则表达式底层实现(重要)

  • 分析底层实现 RegTheory.java

    /**
     * @author shkstart
     * 分析 Java的正则表达式的底层实现(重要)
     */
    public class RegTheory {
        public static void main(String[] args) {
            String str = "1998年12月8日,第二代Java平台的企业版j2ee发布。1999年6月";
            //目标:匹配所有四个数字
            //说明
            //1. /d 表示一个任意的数字
            String regStr = "(\\d\\d)(\\d\\d)";
            //2. 创建模式对象[即正则表达式对象]
            Pattern pattern = Pattern.compile(regStr);
            //3. 创建匹配器
            //说明:创建匹配器 matcher,按照正则表达式的规则 匹配 str字符串
            Matcher matcher = pattern.matcher(str);
            //4. 开始匹配
            /*
             *
             * - \d\d\d\d
             * matcher.find() 完成的任务
             * 1. 根据指定的规则,定位满足规则的子字符串(比如 1998)
             * 2. 找到后,将子字符串的开始索引记录到 matcher对象的属性 int[] groups【初始化20大小,默认-1】
             *   groups[0] = 0,把该子字符串的结束的索引+1的只记录到 groups[1] = 4
             * 3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1的值即4,即下次执行find时,就从4开始匹配
             *
             * matcher.group(0) 分析
             * 源码
             *   public String group(int group) {
             *       if (first < 0)
             *           throw new IllegalStateException("No match found");
             *       if (group < 0 || group > groupCount())
             *           throw new IndexOutOfBoundsException("No group " + group);
             *       if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
             *           return null;
             *       return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
             *   }
             * 1. 根据 groups[0] =0 和 groups[1]=4 的记录的位置,从 str开始截取字符串返回
             *   就是[0,4),就是包含0但是不包含索引为4的位置.
             *
             * 如果再次执行 find方法,仍然按上面来分析
             *  1999 groups[0]=31 groups[1]=35 [31,35) oldLast=35
             *
             * - (\d\d)(\d\d)
             * 什么是分组,比如(\d\d)(\d\d),正则表达式中有() 表示分组,第一个()表示第一组,第二个()表示第二组,...
             *      groups[0]=0 groups[1]=4
             *      记录第一组匹配到的字符串 groups[2]=0 groups[3]=2     =>(19)
             *      记录第一组匹配到的字符串 groups[4]=2 groups[5]=4     =>(98)
             *      如果有更多的分组。。。
             * */
            while (matcher.find()) {
                System.out.println(matcher.group(0));
            }
        }
    }
    

image

  • 小结
    1. 如果正则表达式有() 即分组
    2. 取出匹配的字符串规则如下
    3. group(0) 表示匹配到的子字符串
    4. group(1) 表示匹配到的子字符串的第一组字符串
    5. group(2) 表示匹配到的子字符串的第二组字符串
    6. 。。。但是分组不能越界

正则表达式语法

基本介绍

  • 元字符从功能上大致分为
    1. 限定符
    2. 选择匹配符
    3. 分组和方向引用
    4. 特殊字符
    5. 字符匹配器
    6. 定位符

元字符(Metacharacter)-转义号 \\

  • \\ 符号 说明:在我们使用正则表达式去检索某些特殊字符的时候,需要用到转义符号,否则检索不到结果,甚至会报错。

  • 案例:用 $ 去匹配 "abc\((" 会怎样?用 ( 去匹配 "abc\)(" 会怎样?

  • 再次提示:在 Java的正则表达式中,两个 \\ 代表其他语言中的一个 \ 【java会转义】

    public class Regexp2 {
        public static void main(String[] args) {
            String str = "abc$(abc(123(";
            //匹配
            String regStr = "\\("; // 如果只写个 ( 编译报错
            Matcher matcher = Pattern.compile(regStr).matcher(str);
            while (matcher.find()) {
                System.out.println(matcher.group(0));
            }
        }
    }
    
  • 需要用到转义符号的字符有以下: *** . + ( ) $ \ / ? [ ] { }**

元字符-字符匹配符

image

image

引用实例

  1. [a-z] 说明

    [a-z] 表示可以匹配 a-z 中任意一个字符,比如 [a-z] ,[A-Z] 去匹配 a11c8 会得到什么结果

  2. Java正则表达式默认是区分字母大小写的,如何实现不区分大小写

    • (?i)abc :表示abc都不区分大小写
    • a(?i)abc :表示bc不区分大小写
    • a((?i)b)c :表示只有b不区分大小写
    • Pattern pat = Pattern.compile(regEx,Pattern.CASE.INSENSITIVE)

    [A-Z] :表示可以匹配 A-Z中任意一个字符

    [0-9] :表达可以匹配 0-9中任意一个字符

  3. [^a-z] 说明

    • [^a-z] :表示可以匹配不是 a-z中的任意一个字符,比如

      我们看看 [^a-z]去匹配 a11c8 会得到什么结果? 用 [^a-z]{2} 会得到什么结果?

    [^A-Z] :表示可以匹配不是 A-Z中的任意一个字符

    [^0-9] :表示可以匹配不是 0-9中的任意一个字符

  4. [abcd] :表示可以匹配 abcd 中的任意一个字符

  5. [^abcd] :表示可以匹配不是 abcd中的任意一个字符。

    当然上面的abcd,你可以根据实际清空修改,以适应你的需求

  6. \\d :表示可以匹配 0-9的任意一个数字,相当于 [0-9]

  7. \\D :表示可以匹配不是 0-9中的任意一个数字,相当于 [^0-9]

  8. \\w 匹配大小写英文字符,数字和下划线,相当于 [a-zA-Z0-9]

  9. \\W :相当于[^a-z-A-Z0-9] 是 \W 刚好相反

  10. \\s 匹配任何空白字符【空格,制表符等】

  11. \\S :匹配任何非空白字符,和 \s刚好相反

  12. . 【点】 :匹配出 \n之外的所有字符,如果要匹配,本身则需要使用 \\.

元字符-选择匹配符

image

元字符-限定符

image

image

  1. {n} 说明:n表示出现的次数,比如 a{3},1{4},(\\d)

    但是这里要注意一点,1{3} 去匹配 1111111 的话,会得到什么结果?【111 111】

  2. {n,m} 说明:n表示至少出现的 n次最多m次,比如 a{3,4},1{4,5},\\d{2,5} ,我们看看1{3,4} 去匹配 1111111 的话,会得到什么结果?【1111】

    • 细节:Java匹配默认贪婪匹配,尽可能匹配多的
  3. + 说明

    + 表示出现1次到任意多次,比如 a+,1+,\\d+,我们看看 1+ 去匹配 1111111 的话,会得到什么结果?【1111111 】

  4. * 说明

    * 表示出现 0次到任意多次,比如 a*,1*,\\d* ,我们看看 a1* 去匹配 a111的话,会得到什么结果?【a1】

  5. 说明

    ? 表示出现 0次到1次,比如 a?,1?,\\d,我们看看 a1? 去匹配 a1111 的话,会得到什么结果呐?【a1】匹配 a2111呐?【a】

    当此字符紧随任何其他限定字符【*,+,?,{n},{n,},{n,m}】之后时,匹配模式是“非贪心的”。“非贪心的”匹配搜索到的,尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的,尽可能长的字符串。

        public static void main(String[] args) {
            String content = "hello111111 ok";
    //        String regStr = "\\d{3,4}";  // 默认贪婪匹配 [//找到 = 1111]
            String regStr = "\\d{3,4}?";  // 取消贪婪匹配 后面添加? //[找到 = 111] [找到 = 111]
            Matcher matcher = Pattern.compile(regStr).matcher(content);
            while (matcher.find()) {
                System.out.println("找到 = " + matcher.group(0));
            }
        }
    

元字符-定位符

image

  • han\\b 表示匹配边界的 han【这里的边界是指:被匹配的字符串最后,也可以是空格的子字符串的后面】

分组

image

    public static void main(String[] args) {
        String str = "hanshunping s7788 nn1178mdz";
        String regStr = "\\d\\d\\d\\d"; //匹配4个数组的字符串 【7788 1178】
        /*
         * 下面就是非命名分组
         * 说明
         * 1. matcher.group(0) 得到匹配的字符串
         * 2. matcher.group(1) 得到匹配到的字符串的第一个分组内容
         * 3. matcher.group(2) 得到匹配到的字符串的第二个分组内容
         * 4. ...
         * */
        String regStr2 = "(\\d\\d)(\\d\\d)"; //匹配4个数组的字符串 【7788 1178】
        String regStr3 = "\\d{4}";
        /*
         * 命名分组:既可以给分组取名
         * */
        String regStr4 = "(?<g1>\\d\\d)(?<g2>\\d\\d)"; //匹配4个数组的字符串

        Matcher matcher = Pattern.compile(regStr4).matcher(str);
        while (matcher.find()) {
            System.out.println("找到"+matcher.group(0));
            System.out.println("第一个分组内容"+matcher.group(1));
            System.out.println("第一个分组内容[通过组名]"+matcher.group("g2"));
            System.out.println("第二个分组内容"+matcher.group(2));
        }
    }

image

    //非捕获分组 不能使用!!!  matcher.group(1)
    public static void main(String[] args) {
        String content = "hello 韩顺平教育 jack韩顺平林老师 韩顺平同学hello";

        //1. 找到 韩顺平教育 韩顺平老师 韩顺平同学 子字符串
//        String regStr="韩顺平教育|韩顺平林老师|韩顺平同学";
//        String regStr = "韩顺平(?:教育|林老师|同学)";//和上面等价,不能 matcher.group(1)会报错

        //2. 找到 韩顺平 这个关键字,但是要求只是查找 韩顺平教育 韩顺平老师中包含的韩顺平
//        String regStr = "韩顺平(?=教育|林老师)";

        //3. 找到 韩顺平 这个关键字,但是要求只是查找 不是(韩顺平教育 韩顺平老师)中包含有的韩顺平
        String regStr = "韩顺平(?!教育|林老师)"; //取反

        Matcher matcher = Pattern.compile(regStr).matcher(content);
        while (matcher.find()) {
            System.out.println("找到 = " + matcher.group(0));
        }
    }
  • 非捕获分组 不能使用!!! matcher.group(1)

练习

  1. 汉字
  2. 邮政编码:要求是 1-9开头的一个六位数。比如 123890
  3. qq号码:要求是 1-9开头的一个5位数-10位数
  4. 手机号码:要求必须是 13,14,15,18 开头的11位数
  5. url:
    public static void main(String[] args) {
        String content = "180000000";
        String url = "https://www.bilibili.com/video/BV1Eq4y1E79W?from=search&seid=5816124276308024994&spm_id_from=333.337.0.0";
//        String regStr = "^[\u0391-\uffe5]+$"; //汉字
//        String regStr = "^[1-9]\\d{5}$";//邮政编码
//        String regStr = "^[1-9]\\d{4,9}$";//qq号
//        String regStr = "^1[3|4|5|8]\\d{9}$";//手机号
        /*
         * url:思路
         * 1. 先确定 url的开始部分
         * 2. 然后同 ([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
         * 3. /video/BV1Eq4y1E79W?from=search&seid= 匹配 (\\/[\\w-?=&/%.#]*)?
         * */
        String regStr = "^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";//url 注意[. ? *] 表示匹配就是.本身

        regExp(url, regStr);
    }


    public static void regExp(String content, String regStr) {
        Matcher matcher = Pattern.compile(regStr).matcher(content);
        if (matcher.find()) {
            System.out.println("满足格式");
        } else {
            System.out.println("不满足格式");
        }
    }

正则比表达三个常用类

image

Patten 模式类

  • matches 方法

    public static boolean matches(String regex, CharSequence input) 用于整体匹配,在验证输入的字符串是否满足条件使用
    
    import java.util.regex.Pattern;
    /**
    * 演示matches 方法,用于整体匹配, 在验证输入的字符串是否满足条件使用
    */
    public class PatternMethod {
        public static void main(String[] args) {
            String content = "hello abc hello, 韩顺平教育";
            //String regStr = "hello";
            String regStr = "hello.*";
            boolean matches = Pattern.matches(regStr, content);
    
            System.out.println("整体匹配= " + matches);
        }
    }
    

Matcher 匹配器类

image

image

import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Matcher 类的常用方法
*/
public class MatcherMethod {
    public static void main(String[] args) {
        String content = "hello edu jack hspedutom hello smith hello hspedu hspedu";
        String regStr = "hello";
        Pattern pattern = Pattern.compile(regStr);
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            System.out.println("=================");
            System.out.println(matcher.start());
            System.out.println(matcher.end());
            System.out.println("找到: " + content.substring(matcher.start(), matcher.end()));
        }
        //整体匹配方法,常用于,去校验某个字符串是否满足某个规则
        System.out.println("整体匹配=" + matcher.matches());
        //完成如果content 有hspedu 替换成韩顺平教育
        regStr = "hspedu";
        pattern = Pattern.compile(regStr);
        matcher = pattern.matcher(content);
        //注意:返回的字符串才是替换后的字符串原来的content 不变化
        String newContent = matcher.replaceAll("韩顺平教育");
        System.out.println("newContent=" + newContent);
        System.out.println("content=" + content);
    }
}

PattrnSyntaxException 异常类

分组,捕获,方向引用

  • 给你一段文本,请你找出所有资格数字连载一起的字串,并且这四个数字要满足第一位与第四位相同,第二位和第三位相同,比如1221,5775

image

image

    public static void main(String[] args) {
        String str = "hello 33333 3443 javga14 tom11 jack22 yyy xxx 12331-444555333";

//        String regStr = "(\\d)\\1";
//        String regStr = "(\\d)\\1{4}";
//        String regStr = "(\\d)(\\d)\\2\\1";
        String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
        Matcher matcher = Pattern.compile(regStr).matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group(0));
        }
    }

应用实例

  • 把类似: "我....我要....学学学学....编程java!";通过正则表达式修改成"我要学编程java"

        public static void main(String[] args) {
            String str = "我....我要....学学学学....编程java!";
            /*
             * 1. 找到所有的 .替换成空
             * */
            Matcher matcher = Pattern.compile("\\.").matcher(str);
            str = matcher.replaceAll("");
            System.out.println(str);//我我要学学学学编程java!
            /*
             * 2. 去掉重复的字 我我要学学学学编程java
             * 思路
             * 1. 使用 (.)\\1+
             * 2. 使用方向应用 $1,来替换匹配到的内容
             * */
            //注意:因为正则表达式变化,所以需要重置 matcher
            matcher = Pattern.compile("(.)\\1+").matcher(str); //分组的捕获内容记录到  $1
    
    
            while (matcher.find()) {
                System.out.println(matcher.group(0));//我我 学学学学
            }
            //2. 使用方向应用 $1,来替换匹配到的内容
            str = matcher.replaceAll("$1");
            System.out.println(str);//我要学编程java!
    
            //3. 使用一条语句 去掉重复的字 我我要学学学学编程java!
            //str = Pattern.compile("(.)\\1+").matcher(str).replaceAll("$1");
            //System.out.println(str);
        }
    

String 类中使用正则表达式

替换功能

plublic String replaceAll(String regex,String replacement);
    public static void main(String[] args) {
        String str = "2000年5月,jdk1.3,jdk1.4和j2se1.3相继发布";

        //两个都可以
//        str = str.replaceAll("jdk1.3|jdk1.4", "jdk");
        str = str.replaceAll("jdk1\\.3|jdk1\\.4", "jdk");
        System.out.println(str);//2000年5月,jdk,jdk和j2se1.3相继发布
    }

判断功能

public boolean matches(String regex); 
    public static void main(String[] args) {
        String str = "13955555555";
        boolean matches = str.matches("1(38|39)\\d{8}");
        if (matches) {
            System.out.println("验证成功");
        } else {
            System.out.println("验证失败");
        }
    }

分隔功能

plublic String[] split(String regex);
    //要求按照 # 或者 - 或者 ~ 或者 数字 来分割
    public static void main(String[] args) {
        String str = "hello#abc-jack22smth~北京";
        String[] split = str.split("#|-|~|\\d+");
        System.out.println(Arrays.toString(split));
    }

本站作业

  1. 验证电子邮件是否合法

    • 只能有一个 @
    • @前面是用户名,可以是 a-z A-Z 0-9 _ - 字符
    • @后面是域名,并且域名只能是英文字母。比如 sohu.com qq.com apach.org.cn
    • 写出对应的正则表达式,验证输入的字符串是否为满足规则。
    public static void main(String[] args) {
        String content = "666666@qq.com";
        String regStr = "^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
        if (content.matches(regStr)) {
            System.out.println("匹配成功");
        } else {
            System.out.println("匹配失败");
        }
    }
    
  2. 验证是否是整数或者小数

    提示:这个题要考虑正数和负数,比如 123 -345 34.89 -87.9 -0.01 0.45

      public static void main(String[] args) {
         /*
          * 老师的思路
          * 1. 先写出简单的正则表达式
          * 2. 在逐步的完善【根据各种情况来完善】
          * */
         String content = "00.23"; //0.45
         String regStr = "^[-+]?([1-9]\\d*|0)(\\.\\d+)?$";
         if (content.matches(regStr)) {
             System.out.println("匹配成功 是小数或者正数");
         } else {
             System.out.println("匹配失败");
         }
     }
    
  3. 对一个 url进行解析

    http://www.sohu.com:8080/abc/index/html

    要求的到协议是什么?

    域名是什么?

    端口是什么?

    思路:分组,4组,分别获取到对应的值

    文件名是什么?

        public static void main(String[] args) {
            String content = "https://www.sohu.com:8080/abc//few/few/fwe/index.jsp";
            String regStr = "^([a-zA-Z]+)://([a-zA-Z.]+):(\\d+)[\\w-/]*/([\\w.]+)$";
            Matcher matcher = Pattern.compile(regStr).matcher(content);
            if (matcher.matches()) {
                System.out.println(matcher.group(0));
                System.out.println("协议"+matcher.group(1));
                System.out.println("域名"+matcher.group(2));
                System.out.println("端口号"+matcher.group(3));
                System.out.println("文件"+matcher.group(4));
            }
        }
    

正则表达式速查表



posted @ 2021-10-16 23:42  MikiKawai  阅读(149)  评论(0编辑  收藏  举报