37-正则表达式(概述+符号+对字符串的常见操作(正则的四个功能))+Pattern+Matcher+练习题

一、正则表达式概述

1、正则表达式:用于操作字符串数据,通过一些特定的符号体现

2、String类中

(1)public boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。调用此方法的str.matches(regex)形式与以下表达式产生的结果完全相同:Pattern.matches(regex, str)

注:参数regex:具备一定规则的字符串,即用来匹配字符串的正则表达式

3、正则表达式虽然简化了书写,但阅读性变差了

二、正则表达式的符号

1、字符类

(1)[abc]:字符串的某一位上的字符,必须是:a或b或c

(2)[^abc]:除了a、b、c之外的任何字符

(3)[a-zA-Z]:a到z或A到Z,即 所有大小写字母

(4)[a-d[m-p]]:并集,a到d或m到p,即 [a-dm-p]

(5)[a-z&&[def]]:交集,a到z && d或e或f,即 [def]

(6)[a-z&&[^bc]]:交集,a到z && 非b到c, 即 [ad-z]

(7)[a-z&&[^m-p]]:交集,a到z && 非m到p,即 [a-lq-z]

2、预定义字符

(1).任何字符

(2)\d:数字[0-9]

(3)\D:非数字[^0-9]

(4)\w:单词字符[a-zA-Z_0-9],即 数字、字母(含大小写)、下划线

(5)\W:非单词字符[^\w]

(6)\s:空白字符[ \t\n\x0B\f\r]

(7)\S:非空白字符[^\s]

3、边界匹配器

(1)^:行的开头

(2)$:行的结尾

(3)\b:单词边界(单词与单词之间的空格)

(4)\B:非单词边界

(5)\A:输入的开头

(6)\G:上一个匹配的结尾

(7)\Z:输入的结尾,仅用于最后的结束符(如果有的话)

(8)\z:输入的结尾

4、Greedy数量词

(1)X?:X,一次或一次也没有

(2)X*:X,零次或多次(有也行,没有也行)

(3)X+:X,一次或多次

(4)X{n}:X,恰好n次

(5)X{n, }:X,至少n次

(6)X{n, m}:X,至少n次,但是不超过m次

        String str = "abcabcd";
        String regex = "[abc]+d";
        System.out.println(str.matches(regex)); //true

三、正则表达式对字符串的常见操作(正则的四个功能)

!!!匹配、切割、替换这些功能,底层用的都是Pattern对象的方法。因为正则模式作用于字符串只有一个对象:Pattern

eg:字符串中的matches(regex)方法,底层用的就是正则表达式中正则对象的matches()方法Pattern.matches(regex, str)

1、匹配:使用的是String类中的matches(regex)方法

                public boolean matches(String regex)

        //匹配手机号码是否正确
        String phoneNum = "13522943355";
//        String regex = "1[358][0-9]{9}";
        // \d:表示[0-9]的数字
        //反斜杠(\)在字符串中会自动对其后的字符进行转义,所以要使用\\d
        String regex = "1[358]\\d{9}";
        boolean result = phoneNum.matches(regex);
        System.out.println(phoneNum + ":" + result);

2、切割:使用的是String类中的split(regex)方法

                public String[] split(String regex)

        //用空格切割姓名
        String str = "zhangsan xiaoqiang zhaoliu";
        String[] names = str.split(" ");
        for (String name : names) {
            System.out.println(name);
        }

问:如果姓名之间有多个空格,怎么切割?

        //用一个或多个空格切割姓名
        String str = "zhangsan     xiaoqiang      zhaoliu";
        // " +":匹配一个或多个空格
        String[] names = str.split(" +");
        for (String name : names) {
            System.out.println(name);
        }
        //用 . 切割姓名
        String str = "zhangsan.xiaoqiang.zhaoliu";
        // ".":在正则表达式中是一个特殊符号,用 \\. 来转义
        String[] names = str.split("\\.");
        for (String name : names) {
            System.out.println(name);
        }
        //用叠词切割姓名
        String str = "zhangsantttttxiaoqiangmmmzhaoliu";
        //()代表一个组,\\1代表与第一组相同
        //(.)\\1+:有一个或多个与第一组相同的内容,而第一组是任意字符
        String[] names = str.split("(.)\\1+");
        for (String name : names) {
            System.out.println(name);
        }

分析:叠词,第二位的内容与第一位一致,意味着第一位的内容被后面复用。如何实现复用?

补充:

(1)正则中,复用的封装方式:小括号(Java中基本的复用形态是:封装成函数)。封装体没有名字,但会自动编号,从1开始。这种封装形式称为组

即 正则规则中用于封装的形式是小括号。为了能使用这种封装形式,没有名字,但有编号。写一个就是1编号,再写一个就是2编号

(2)(.):括号()就是组,该组的内容是 . ,即为任意字符。

         \\1:使用第一组的内容。直接用编号n就代表使用第n组的内容,但编号n需要被转义成\\n

         (.)\\1+:一个任意字符,后面有一个或多个与之相同的字符

(3)正则中的组按左括号来编号,有几个左括号就有几个组

eg:((A)(B(C))):有四个组,分别是:

        1:((A)(B(C)))

        2:\A

        3:(B(C))

        4:(C)

(4)正则中,封装成组来应用,提高规则的复用性

3、替换:使用的是String类中的replaceAll(regex, replacement)方法

                public String replaceAll(String regex, String replacement)

        //将叠词替换成#
        String str = "zhangsanttttxiaoqiangmmmmmmmzhaoliu";
        //使用replaceAll()方法,第一个参数是需要被替换的内容(正则表达式),第二个参数是替换后的内容
        str = str.replaceAll("(.)\\1+", "#");
        System.out.println("str = " + str);   //str = zhangsan#xiaoqiang#zhaoliu
        //将叠词替换成一个字符(eg:将多个t变成一个t,将多个m变成一个m)
        String str = "zhangsanttttxiaoqiangmmmmmmmzhaoliu";
        //$n:获取前一个参数中的第n组的内容
        //$:在其他参数中可以对前一个参数中的已有的正则中的规则进行获取
        str = str.replaceAll("(.)\\1+", "$1");
        System.out.println("str = " + str);   //str = zhangsantxiaoqiangmzhaoliu

补充:

(1)一个方法的第一个参数带着正则,第二个参数想使用第一个参数正则中的内容,用$n来表示

(2)$n:获取前一个参数中的第n组的内容

(3)$:在其他参数中可以对前一个参数中的已有的正则中的规则进行获取

        //15800001111 --> 158****1111
        String str = "15800001111";
        //将号码分成三部分。$n是获取前一个参数中第n组的内容,所以 \\d{3} 要放在一个组中,用(\\d{3})
        str = str.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
        System.out.println("str = " + str);

4、获取:使用匹配器Matcher中的group()方法

                 String group()

        //获取str中由3个字母组成的单词
        String str = "da jia hao , ming tian bu fang jia !";
        //三个字母组成的单词,单词的两边要有单词边界\\b
        String regex = "\\b[a-zA-Z]{3}\\b";
        //将正则封装成对象
        Pattern p = Pattern.compile(regex);
        //通过正则对象获取匹配器对象
        Matcher m = p.matcher(str);
        //用Matcher对象的方法对字符串进行操作
        //要获取三个字母组成的单词,先用find()查找,再用group()获取
        while (m.find()) {
            System.out.println(m.group());
            //可以获取位置(包含头 不包含尾),相当于indexOf()
            System.out.println(m.start() + "--" + m.end());
        }

四、java.util.regex.Pattern

1、public final class Pattern:正则表达式的编译表示形式(需要将正则封装成对象)

2、指定为字符串的正则表达式必须首先被编译为此类的实例(将正则表达式封装成对象)。然后,可将得到的模式(正则规则)用于创建Matcher对象(匹配器),依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式

注:

(1)将正则封装成对象(封装对象,正则没有对字符串进行操作)

(2)建立一个匹配器对象

(3)正则只负责表达式的封装,要想用表达式操作字符串,必须要获取匹配器对象。匹配器对象用规则操作字符串,按规则对字符串进行匹配。匹配的结果都在匹配器中

        //将正则规则进行对象的封装
        Pattern p = Pattern.compile("a*b");
        //通过正则对象的matcher()方法和字符串关联,获取要对字符串操作的匹配器对象Matcher
        Matcher m = p.matcher("aaaaab");
        //通过Matcher匹配器对象的方法对字符串进行操作
        boolean b = m.matches();

3、方法

(1)static Pattern compile(String regex):将给定的正则表达式编译到模式中

(2)static Pattern compile(String regex, int flags):将给定的正则表达式编译到具有给定标志的模式中

(3)int flags():返回此模式的匹配标志

(4)Matcher matcher(CharSequence input):创建匹配给定输入与此模式的匹配器

(5)static boolean matches(String regex, CharSequence input):编译给定正则表达式并尝试将给定输入与其匹配

        //以下两行代码的行为完全相同
        Pattern.matches(regex, input);  //便捷方式
        Pattern.compile(regex).matcher(input).matches();

注:如果要多次使用一种模式,编译一次后重用此模式比每次都调用此方法效率更高

(6)String pattern():返回在其中编译过此模式的正则表达式

(7)static String quote(String s):返回指定String的字面值模式String。此方法产生一个String,可以将其用于创建与字符串s匹配的Pattern,就好像它是字面值模式一样。输入序列中的元字符和转义序列不具有任何特殊意义

(8)String[] split(CharSequence input):围绕此模式的匹配拆分给定输入序列

(9)String[] split(CharSequence input, int limit):围绕此模式的匹配拆分给定输入序列

(10)String toString():返回此模式的字符串表示形式。此为在其中编译过此模式的正则表达式

五、java.util.regex.Matcher

1、public final class Matcher:通过解释Pattern(正则表达式)对character sequence(字符序列)执行匹配操作的引擎(Matcher是真正用正则表达式操作字符串的对象)

2、通过调用模式的matcher()方法从模式创建匹配器。创建匹配器后,可以使用它执行三种不同的匹配操作:

(1)matches()方法尝试将整个输入序列与该模式匹配

(2)lookingAt()尝试将输入序列从头开始与该模式匹配

(3)find()方法扫描输入序列以查找与该模式匹配的下一个子序列

3、部分方法

(1)boolean matches():尝试将整个区域与模式匹配。如果匹配成功,则可以通过start()、end()和group()方法获取更多信息

(2)int start():返回以前匹配的初始索引(返回第一个匹配字符的索引)

(3)int start(int group)

(4)int end():返回最后匹配字符之后的偏移量

(5)int end(int group)

(6)boolean find():尝试查找与该模式匹配的输入序列的下一个子序列(当且仅当输入序列的子序列匹配此匹配器的模式时才返回true)。此方法从匹配器区域的开头开始,如果该方法的前一次调用成功了并且从那时开始匹配器没有被重置,则从以前匹配操作没有匹配的第一个字符开始

(7)boolean find(int start)

(8)String group():返回由以前匹配操作所匹配的输入子序列。对于具有输入序列s的匹配器m,表达式m.group()和s.substring(m.start(), m.end())是等效的

(9)String group(int group)

(10)boolean lookingAt():尝试将从区域开头开始的输入序列与该模式匹配。与matches()方法类似,此方法始终从区域的开头开始;与之不同的是,它不需要匹配整个区域

4、find():查找,group():获取。group()获取之前要先find()查找,否则报错 IllegalStateException: No match found。所以,要先拿规则regex到字符串str中匹配。find()方法的返回值是boolean类型,想要获取全部,先要用while(find())循环,在循环体里面group()获取

        //获取str中由3个字母组成的单词
        String str = "da jia hao , ming tian bu fang jia !";
        //三个字母组成的单词,单词的两边要有单词边界\\b
        String regex = "\\b[a-zA-Z]{3}\\b";
        //将正则封装成对象
        Pattern p = Pattern.compile(regex);
        //通过正则对象获取匹配器对象
        Matcher m = p.matcher(str);
        //用Matcher对象的方法对字符串进行操作
        //要获取三个字母组成的单词,先用find()查找,再用group()获取
        while (m.find()) {
            System.out.println(m.group());
            //可以获取字符串的位置(包含头 不包含尾),相当于indexOf()
            System.out.println(m.start() + "--" + m.end());
        }

5、匹配器Matcher也可以完成匹配、替换等操作,但字符串String中的匹配、替换等方法更为简单。但获取功能只能用匹配器Matcher中的方法完成

六、练习题

1、治疗口吃:我我我...要...要要...学学学学..编编...编..程....程程  -->  我要学编程

        //治疗口吃:我我我...要...要要...学学学学..编编...编..程....程程 --> 我要学编程
        //用替换
        String str = "我我我...要...要要...学学学学..编编...编..程....程程";
        //先将 . 都去掉。用\\.对.进行转义
        str = str.replaceAll("\\.+", "");
        System.out.println("str = " + str);
        /**
         * 去掉叠词
         * (.):一组,该组中的内容为任意字符
         * \\1:使用第一组的内容
         * \\1+:使用第一组的内容一次或多次
         * (.)\\1+:一个任意字符,后面有一个或多个与之相同的字符
         * $1:使用第一个参数中第一组的内容
         */
        str = str.replaceAll("(.)\\1+", "$1");
        System.out.println("str = " + str);

2、对IP地址排序(按IP地址分类排序,每段数字越小越靠前):192.168.10.34    127.0.0.1    3.3.3.3    105.70.11.55

        //对IP地址排序(按IP地址分类排序,每段数字越小越靠前):192.168.10.34    127.0.0.1    3.3.3.3    105.70.11.55
        String str = "192.168.10.34    127.0.0.1    3.3.3.3    105.70.11.55";
        /*
        //将IP地址切出,用一个或多个空格切
        String[] ips = str.split(" +");
        */
        /**
         * 使用TreeSet对象排序 -- 自动排序
         * 问题:TreeSet默认按照字符串排序,而需求是按照数值大小排序
         * 错误原因:每一段的位数不一样。如果每一段的位数相同,结果就是正确的
         * 做法:前面补0,每段都加2个零(保证至少是3位),再截取(保留)后三位
         *
         * 即 为了让IP可以按照字符串顺序比较,只要让IP的每一段位数相同即可。所以,补0
         * 按照每一位所需最多0进行补充,每一段都加2个0
         */
        //每段都补2个0
        str = str.replaceAll("(\\d+)", "00$1");
        //每段保留后三位
        str = str.replaceAll("0*(\\d{3})", "$1");
        //将IP地址切出,用一个或多个空格切
        String[] ips = str.split(" +");

        //将切出的字符串放入TreeSet中自动排序
        TreeSet<String> ts = new TreeSet<String>();
        for (String ip : ips) {
            ts.add(ip);
        }

        //遍历TreeSet取出其中的元素
        for (String ip : ts) {
            //将每段前面的0去掉
            ip = ip.replaceAll("0*(\\d+)","$1");
            System.out.println("ip = " + ip);
        }

3、对邮件地址校验

        //对邮件地址校验
        //匹配规则
        String mail = "abc1@sina.com.cn";
        //是校验,不是注册,不用判断位数
//        String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3}){1,3}";
        //笼统式匹配
        String regex = "\\w+@\\w+(\\.\\w+)+";
        boolean b = mail.matches(regex);
        System.out.println("mail : " + b);

注:正则的阅读性差,而且写完后还需要不断地进行验证。所以,在实际开发中,会将正则的校验封装成方法,将需要校验的字符串作为参数传到方法中进行验证

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值