• 正则表达式


    一、代码逻辑

    public class Test2 {
    
        public static void main(String[] args) {
    
            String s = "一百多年后,四色问题仍未解决。1969年,Heinrich Heesch发表了一个用计算机" +
                    "解决此问题的方法。1976年,阿佩尔(Appel)和哈肯(Haken)借助计算机给出了一个证明," +
                    "此方法按某些性质将所有地图分为1936类并利用计算机,运行了1200个小时,验正了它们可" +
                    "以用四种颜色染色。四色定理是第一个主要由电脑证明的理论,这一证明并不被所有的数学家接受" +
                    ",因为采用的方法不能由人工直接验1111证。最终,人们必须对电脑编译的正确性以及运行这一程序" +
                    "的硬件设备充分信任。主要是因为此证明缺乏数学应1112有的规范,以至于有人这样评论“一个好的数" +
                    "学证明应6666当像一首诗——而3333这纯粹是一本电话簿!";
    
            //匹配所有四个数字
            // \\d:表示任意一个数字  4个的话表示任意连在一起的4个数字
            String regStr = "\\d\\d\\d\\d";
            //模式对象(正则表达式对象)
            Pattern pattern = Pattern.compile(regStr);
            //创建匹配器
            Matcher matcher = pattern.matcher(s);
            //开始循环匹配
            while (matcher.find()){
                System.out.println("找到: " + matcher.group(0));
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    二、底层逻辑分析

    1.matcher.find()

    matcher.find()完成的任务
     1.根据匹配规则,定位满足规则的子字符串(比如1969)
     2.找到后,将子字符串的开始索引记录到 matcher 对象的属性 int[] groups,groups[0]=0,
       把该子字符串的结束的索引+1的值记录到groups[1]=4
       如果正则表达式有分组,则groups[2] groups[3]记录第一组字符串的开始和结束位置
       其余组以此类推
     3.同时记录 oldLast 的值,即该子字符串的结束的索引+1的值,即下次执行find时,就从4开始匹配
     4.当一次匹配成功后,下一次groups[0]存储下一个符合条件的字符串的开始位置,groups[1]存储的
       是其结束位置  周而复始直到文本遍历结束
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.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 的记录的位置,从s中截取字符串返回
    2.group(0)表示匹配到的子字符串
      group(1)表示匹配到的子字符串的第一组子串
      以此类推
      但是分组的数不能越界
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三、正则表达式语法

    1.转义符

    \\
    在我们使用正则表达式去检索某些特殊字符时,需要用到转移符号,负责检索不到
    在java的正则表达式中,两个 \\ 代表其他语言中的一个 \
    
    需要用到转移符号的字符有如下字符:
    . * + ( ) $ / \ ? [ ] ^ { }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
        /**
         * 演示转义字符的使用
         */
        private static void test(){
            String s = "abc.$(abc.(123(";
            //匹配 (
            String regStr = "\\(";
            //模式对象(正则表达式对象)
            Pattern pattern = Pattern.compile(regStr);
            //创建匹配器 在s中根据模式对象匹配
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.println(matcher.group(0));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.字符匹配符

    符号含义示例解释匹配输入
    [ ]可接收的字符列表[abcew]匹配a b c e w中的任意一个字符
    [^]不接收的字符列表[^abc]匹配除a b c之外的任意一个字符,包括数字和特殊字符
    -连接符a-z匹配任意单个小写字母
    .匹配除 \n 以外的任何字符a…b匹配以a开头,b结尾,中间包括2个任意字符的长度为4的字符串aaab #a1o
    \\d匹配单个数字字符,相当于[0-9]\\d{3}(\\d)?包含3个或4个数字的字符串123 4351
    \\D匹配单个非数字字符,相当于[^0-9]\\D(\\d)*以单个非数字字符开头,后接任意个数字字符串a #124
    \\w匹配单个数字、大小写字母、下划线字符,相当于[0-9a-zA-Z]\\d{3}\\w{4}以3个数字字符开头的长度为7的字符串123nmed 4322swe
    \\W匹配单个非 数字、大小写字母下划线字符,相当于[^0-9a-zA-Z]\\W+\\d{2}以至少1个非数字字母字符开头,2个数字字符结尾的字符串#23 %()10
    \\s匹配任何空白字符(空格 制表符等)\\s
    \\S匹配任何非空白字符,与\\s相反\\S
    java匹配字母字符时默认区分大小写,可以使用以下方法设置不区分大小写
    (1)(?i)
        (?i)abc 表示abc都不区分大小写
        a(?i)bc 表示bc不区分大小写
        a((?i)b)c 表示只有b不区分大小写
    (2)Pattern.CASE_INSENSITIVE 在创建模式对象时加这个
        Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
        /**
         * 演示字符匹配符的使用
         */
        private static void test1(){
            String s = "a11c8AQ4Wz";
            //匹配 a-z 的任意1个字符
            String regStr = "[a-z]";
            //模式对象(正则表达式对象) Pattern.CASE_INSENSITIVE表示不区分字母的大小写
            Pattern pattern = Pattern.compile(regStr,Pattern.CASE_INSENSITIVE);
            //创建匹配器 在s中根据模式对象匹配
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.println(matcher.group(0));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.选择匹配符

    符号含义示例解释
    |匹配 | 之前或之后的表达式ab|cdab或者cd
        /**
         * 演示选择匹配符
         */
        private static void test2(){
            String s = "1231one一";
            //匹配 1 one 一 
            String regStr = "1|one|一";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //创造匹配器
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.println(matcher.group(0));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.限定符

    用于指定其前面的字符和组合项连续出现多少次
    
    • 1
    符号含义示例说明匹配输入
    *指定字符重复0次或者n次(无要求)(abc)*仅包含任意个abc的字符串abc abcabc
    +指定字符重复1次或者n次m+(abc)*以至少1个m开头,后接任意个abc的字符串m mabc mabcabc
    指定字符重复0次或者1次m+abc?以至少1个m开头,后接ab或abc的字符串mab mabc mmab
    {n}只能输入n个字符[abcd]{3}由abcd中字母组成的任意长度为3的字符串abc dbc adc
    {n,}指定至少n个匹配[abcd]{3,}由abcd中字母组成的任意长度不小于3的字符串aab dbc aaadca
    {n,m}指定至少n个但不多于m个匹配[abcd]{3,5}由abcd中字母组成的任意长度不小于3 不大于5的字符串abc abcd aaaaa cdba
    java中默认是贪婪匹配:a{3,5} 在匹配过程中尽量多的匹配a,匹配优先级:aaaaa > aaaa > aaa
    
    • 1
        /**
         * 限定符演示
         */
        private static void test3(){
            String s = "12adee3421dsacc543";
            //匹配1个数字或者多个数字
            String regStr = "\\d+";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //创造匹配器
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.println(matcher.group(0));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.定位符

    规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置
    
    • 1
    符号含义示例说明匹配输入
    ^指定起始字符1+[a-z]*以至少1个数字开头,后接任意个小写字母的字符串123 3ac
    $指定结束字符2+[a-z]+$以至少1个数字开头,并以至少1个小写字母结尾的字符串11a
    \\b匹配目标字符串的边界hello\\b边界:子串间有空格,或者是目标字符串的结束位置
    \\B匹配目标字符串的非边界hello\\B和\\b的含义相反

    6.分组

    常用分组构造形式说明
    (pattern)非命名捕获 捕获匹配的子字符串 编号为0的第一个捕获是由整个正则表达式模式匹配的文本,其它捕获结果则根据左括号的顺序从1开始自动编号
    (?pattern)命名捕获 将匹配的子字符串捕获到一个组名或者编号名称中。用于name的字符串不能包含任何标点符号,并且不能以数字开头。可以使用单引号代替<>
        /**
         * 分组演示
         */
        private static void test4(){
            String s = "1234adace2314de9999";
            String regStr = "(\\d\\d)(\\d\\d)";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //构造匹配器
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.print("整个内容: " + matcher.group(0) + "   ");
                System.out.print("第1个分组内容: " + matcher.group(1) + "   ");
                System.out.println("第2个分组内容: " + matcher.group(2));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    7.反向引用

    反向引用:圆括号的内容被捕获后,可以在这个括号后被使用
            这种引用既可以是在正则表达式的内部,也可以是在正则表达式的外部
            内部反向引用使用 \\分组号 ,外部反向引用使用 $分组号
    
    举例:
    匹配两个连续的相同数字:(\\d)\\1
    匹配五个连续的相同数字:(\\d)\\1{4}
    匹配个位与千位相同,十位与百位相同的数:(\\d)(\\d)\\2\\1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
        /**
         * 找出一段文本中,找出个位与千位相同,十位与百位相同的数
         */
        private static void test11(){
            String s = "1111and2332sdniew1234kd13";
            String regStr = "(\\d)(\\d)\\2\\1";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //匹配器对象
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.println("找到: " + matcher.group(0));
            }
        }
    
    
        /**
         * 在字符串中检索商品编号,形如:12321-333999111
         * 要求满足前面是一个5位数,然后一个 - ,然后是一个九位数,连续的每三位要相同
         */
        private static void test12(){
            String s = "12321-333999111";
            String regStr = "\\d{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //匹配器对象
            Matcher matcher = pattern.matcher(s);
            while (matcher.find()){
                System.out.println("找到: " + matcher.group(0));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    四、常用的类

    1.Pattern类

        /**
         * matches方法
         * 用于整体匹配 验证输入的字符串是否满足条件
         */
        private static void test10(){
            String s = "hello welcome to china";
            String regStr = ".*come.*";
            boolean matches = Pattern.matches(regStr, s);
            System.out.println("整体匹配: " + matches);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.Matcher类

    五、应用实例

    1.验证输入的字符串是不是汉字

        /**
         * 验证输入的字符串是否全是汉字
         */
        private static void test5(){
            String s = "为中华之崛起而读书";
            String regStr = "^[\u0391-\uffe5]+$";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //构造匹配器
            Matcher matcher = pattern.matcher(s);
            if (matcher.find()){
                System.out.println("满足格式");
            } else {
                System.out.println("不满足格式");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.验证邮政编码

        /**
         * 验证邮政编码
         * 要求:是1-9开头的一个六位整数 比如:123540
         */
        private static void test6(){
            String s = "123540";
            String regStr = "^[1-9]\\d{5}$";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //匹配器对象
            Matcher matcher = pattern.matcher(s);
            if (matcher.find()){
                System.out.println("满足格式");
            } else {
                System.out.println("不满足格式");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.验证QQ号

        /**
         * 验证QQ号
         * 要求:1-9开头的一个(5位数-10位数)
         */
        private static void test7(){
            String s = "1904459082";
            String regStr = "^[1-9]\\d{4,9}$";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //匹配器对象
            Matcher matcher = pattern.matcher(s);
            if (matcher.find()){
                System.out.println("满足格式");
            } else {
                System.out.println("不满足格式");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4.验证手机号码

        /**
         * 验证手机号码
         * 要求:必须以 13 14 15 18 开头的11位数
         */
        private static void test8(){
            String s = "15109546897";
            String regStr = "^1[3|4|5|8]\\d{9}$";
            //模式对象
            Pattern pattern = Pattern.compile(regStr);
            //匹配器对象
            Matcher matcher = pattern.matcher(s);
            if (matcher.find()){
                System.out.println("满足格式");
            } else {
                System.out.println("不满足格式");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.验证URL地址

        /**
         * 验证URL地址
         * 分析:
         *      1.先确定url开始的部分 https:// 或者 http://  有些URL没有这一部分
         *      2.然后通过 ([\w-]+\.)+[\w-]+ 来匹配 leetcode.cn
         *      3.通过 [\w-]+(\/[\w-?=&/.]*)?$ 来匹配
         */
        private static void test9(){
    
            String s = "https://leetcode.cn/problemset/algorithms/?status=NOT_STARTED&page=1";
            String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/.]*)?$";
            //模式对象
           Pattern pattern = Pattern.compile(regStr);
           //匹配器对象
            Matcher matcher = pattern.matcher(s);
            if (matcher.find()){
                System.out.println("满足格式");
            } else {
                System.out.println("不满足格式");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    6.结巴程序

    例如 我...是...你你..爸爸爸爸...
    输出 我是你爸爸
    
    • 1
    • 2
        /**
         * 处理结巴程序
         */
        private static void test13(){
    
            String s = "我我我...是是是是是...你你..爸爸爸爸...";
            //1.去掉所有的 "."
            Pattern pattern = Pattern.compile("\\.");
            Matcher matcher = pattern.matcher(s);
            //将所有的 . 全部替换掉
            s = matcher.replaceAll("");
    
            /**
             * 2.去掉重复的字:
             *             (1)使用 (.)\\1+ 找到连续相同的
             *             (2)使用反向引用 $1 来替换匹配到的内容
             */
            pattern = Pattern.compile("(.)\\1+");
            matcher = pattern.matcher(s);
            while (matcher.find()){
    
            }
            //使用反向引用 $1 来替换匹配到的内容
            s = matcher.replaceAll("$1");
            System.out.println(s);
    
            //也可以只使用一条语句代替步骤二
            s = Pattern.compile("(.)\\1+").matcher(s).replaceAll("$1");
            System.out.println(s);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    6.String类的替换分割匹配

        /**
         * 将 JDK1.3 JDK1.4 全部替换为 JDK
         */
        private static void test14(){
            String s = "JDK1.3是JDK1.4之前的一个版本,JDK1.3与JDK1.4之间有一些区别";
            s = s.replaceAll("JDK1\\.3|JDK1\\.4","JDK");
            System.out.println(s);
        }
    
    
        /**
         * 验证一个手机号,要求必须以 138 139 开头的
         */
        private static void test15(){
            String s = "13811112222";
            if (s.matches("1(38|39)\\d{8}")){
                System.out.println("验证成功");
            } else {
                System.out.println("验证失败");
            }
        }
    
    
        /**
         * 分割 按照 # - ~ 数字 来分割
         */
        private static void test16(){
            String s = "hello-boy~welcome#1to3china";
            String[] splits = s.split("#|-|~|\\d+");
            for (String cur : splits){
                System.out.print(cur + " ");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    1. 0-9 ↩︎

    2. 0-9 ↩︎

  • 相关阅读:
    43.228.64.X游戏盾在业务高峰时间段,出现用户大量掉线问题,可能是什么原因导致
    基于JAVA计算机类课程实验平台计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    哦麦艾斯!AI设计的丑衣服将引领时尚?数据结构与算法代码面试题;将文件藏在图片里的隐写工具;蒙古语语音合成语料库
    文本词频统计的应用——以微词云平台为例
    【MATLAB源码-第86期】基于matlab的QC-LDPC码性能仿真,输出误码率曲线。
    云原生分级SLA
    经典再现!阿里大牛亲码千页Java异步实战手册,实战太香了!
    基于javaweb的旅游网站系统
    Fair下发产物-布局DSL生成原理
    剑指offer_II_119 最长连续序列
  • 原文地址:https://blog.csdn.net/weixin_56680764/article/details/133764073