JAVASE 正则表达式

正则表达式

一、正则表达式概念及快速入门

1、概念

​ 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合条件的子串等。

​ 如:平时我们利用String类来处理字符串信息的时候,需要对一个一个字符进行处理,如果想验证一大串信息是否满足要求时,想要达到各种各样的要求就非常的复杂,但正则表达式的使用就会使我们处理字符串非常的方便,正则表达式将会是我们处理各种字符串的利器。

2、正则表达式快速入门




import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 体验正则表达式威力,体现正则表达式处理文本带来的威力
 * @author 风之诗
 */
public class Regexp_ {
        public static void main(String[] args) {
                /**
                 * 三段内容,分别是java的基本介绍,html网页代码,一些地址
                 */
                String content1="1995年,互联网的蓬勃发展给了Oak机会。业界为了使死板、单调的静态网页能够“灵活”起来,急需一种" +
                        "软件技术来开发一种程序,这种程序可以通过网络传播并且能够跨平台运行。于是,世界各大IT企业为此纷纷投入了大量的人力、物力和财力。这个时候,Sun公司想起了那个被搁置起来很久的Oak,并且重新审视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编写的,所以非常小,特别适用于网络上的传输系统,而Oak也是一种精简的语言,程序非常小,适合在网络上传输。Sun公司首先推出了可以嵌入网页并且可以随同网页在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术)" +
                        ",并将Oak更名为Java(在申请注册商标时,发现Oak已经被人使用了,再想了一系列名字之后,最" +
                        "终,使用了提议者在喝一杯Java咖啡时无意提到的Java词语)。5月23日,Sun公司在Sun world会" +
                        "上正式发布Java和HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等" +
                        "各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Ja" +
                        "va平台。";
                String content2="私有地址(Private address)属于非注册地址,专门为组织机构内部使用。\n" +
                        "以下列出留用的内部私有地址\n" +
                        "A类 10.0.0.0--10.255.255.255\n" +
                        "B类 172.16.0.0--172.31.255.255\n" +
                        "C类 192.168.0.0--192.168.255.255";
                       
                //传统方法:使用遍历,代码量大,效率不高
                //正则表达式技术
                //1、提取文章中的所有英文单词
                //2、提取文章中的所有数字
                //3、提取文章中的所有数字和英文单词
                //4、匹配url


                //1.先创建一个pattern对象:模式的对象,正则表达式的对象
                Pattern pattern1 = Pattern.compile("[a-zA-Z]+");
                //Pattern pattern2 = Pattern.compile("[0-9]+");
                //Pattern pattern3 = Pattern.compile("([0-9])+|([a-zA-Z]+)");
                //Pattern pattern4 = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
                //2.创建一个匹配器对象:就是按照pattern对象的模式,到content的文本中进行匹配和解释的对象
                Matcher matcher = pattern.matcher(content1);
                //matcher.find()方法,即在文本中寻找字符串,找到返回true,未找到返回false
                //匹配字符串
                int no=0;
                while(matcher.find()){
                        //匹配内容,文本,放到m.gruop(0)中
                        System.out.println("找到: "+(++no)+" "+ matcher.group(0));
                }
        }

}

二、正则表达式底层源码分析

1、不分组以及分组

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 源码分析以及正则表达式的分组的分析
 */
public class RegTheory {
    public static void main(String[] args) {
        String content="1995年,互联网的蓬勃发展给了Oak机会。业界为了使死板、单调的静态网页能够“灵活”起来,急需一种" +
                "软件技术来开发一种程序,这种程序可以通过网络传播并且能够跨平台运行。于是,世界各大IT企业为此纷纷投入了大量的人力、物力和财力。这个时候,Sun公司想起了那个被搁置起来很久的Oak,并且重新审视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编写的,所以非常小,特别适用于网络上的传输系统,而Oak也是一种精简的语言,程序非常小,适合在网络上传输。Sun公司首先推出了可以嵌入网页并且可以随同网页在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术)" +
                ",并将Oak更名为Java(在申请注册商标时,发现Oak已经被人使用了,再想了一系列名字之后,最" +
                "终,使用了提议者在喝一杯Java咖啡时无意提到的Java词语)。5月23日,Sun公司在Sun world会" +
                "上正式发布Java和HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等" +
                "各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Ja" +
                "va平台。";
        //  \\d表示任意一个数字
        String RegStr="(\\d\\d)(\\d\\d)";//正则表达式模式字符串
        //1、创建一个pattern对象来传入正则表达式
        Pattern pattern = Pattern.compile(RegStr);
        //2、创建matcher匹配器 传入所需匹配字符串的内容 按照pattern定义模式的方法去匹配
        Matcher matcher = pattern.matcher(content);
        //3、开始匹配
        while(matcher.find()){
            //有括号时的匹配规则:
            //group(0)代表取出全部
            //group(1)代表取出第一组
            //group(2)代表取出第二组
            //注意:1、匹配的组数不能越界,有几组就取出几组。
            //     2、如果没分组,就只有group(0)
            System.out.println("找到: "+matcher.group(0));
            System.out.println("第一组()匹配到的值: "+matcher.group(1));
            System.out.println("第二组()匹配到的值: "+matcher.group(2));
        }
    }
}

解析:

/**
 *
 * matcher.find()完成的任务 (考虑分组):定位所匹配到的位置,然后交给.group转化为字符串
 *find()过程:
 * 分组:正则表达式中用()来分组,如(\d\d)(\d\d),第一个()表示第一组,第二个()表示第二组
 * 1、根据指定的规则,定位满足规则的子字符串(如1998)
 * 2、找到后,将子字符串的索引位置记录到matcher对象的属性 int[] groups;
 *      2.1 groups[0]=0,将字符串结束的索引+1并记录在groups[1]=4(因为后面group方法寻找的下标不包含最右边)
 *      2.2 记录1组()匹配到的字符串的索引groups[2]=0 gruops[3]=2
 *      2.3 记录2组()匹配到字符串的索引gruops[4]=2 gruops[5]=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的位置,从content中截取子字符串返回
 *     就是从[0,4)包含的子字符串(不包含4)
 *  2、同理:当gruops(1)就是根据groups[2]=0和groups[3]=2的位置,从第一组括号里面提取下标转化为字符串,取出第一个分组
 */

三、正则表达式语法

1、字符匹配符:匹配自己想要选择的字符串的一种格式

/**
 * //d:匹配0-9数字
 * //D:匹配非0-9数字
 * //w:匹配任意英文字符,数字以及下划线
 * //W:匹配非任意英文字符,数字以及下划线
 * //s:匹配任何空白字符(空格、制表符)
 * //S:匹配任何非空白字符
 * . :匹配除任何\n之外的所有字符
 * -:连字符 表示一个字符范围  实例:A-Z 任意单个大写字母
 * []:可接受的字符列表 匹配括号里面的字符 实例:[abcd]--->接受abcd其中任意一个字符 [a-zA-Z]---->接受a-z,A-Z其中任何一个字符
 * [^]:不可接受字符列表 匹配非括号里面的字符 实例:	[^abcd]--->接受除了abcd外的其它字符
 
 !!转义!!
 *在其他语言中,\\ 表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。
*在 Java 中,\\ 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。
*所以,在其他的语言中(如 Perl),一个反斜杠 \ 就足以具有转义的作用,而在 Java 中正则表达式中则需要有两个反斜杠才能被解析为其他*语言中的转义作用。也可以简单的理解在 Java 的正则表达式中,两个 \\ 代表其他语言中的一个 \,这也就是为什么表示一位数字的正则表达 *式是 \\d,而表示一个普通的反斜杠是 \\。
*System.out.print("\\");    // 输出为 \
*System.out.print("\\\\");  // 输出为 \\


 */

2、选择匹配符:相当于或

|: 如果匹配某个字符是选择性的,则需要用|来匹配。 实例:ab|cd 匹配ab或者cd

3、限定符:用于指定某一段字符连续出现多少次

/**
 *
 *  *:表示匹配指定字符的0到n次  (abc)*-->abc、abcabc
 *  +:表示匹配指定字符的1到n次  m(abc)+-->mabc、mabcabc
 *  ?:表示匹配指定字符的0或1次   mabc?--->mab、mabc
 *  {n}:匹配长度为n的指定字符串  [abcd]{3}---->abc、acd、adc、bcd。。。。
 *  {n,}:匹配长度最少为n的指定字符串  [abcd]{4,}---->abcd、aabcd、bcdaaaa
 *  {a,b}:匹配长度最少为a最多为b的指定字符串 [abcd]{1,2}---->ab、a、cd
 */

贪婪匹配与非贪婪匹配机制:

(1)、贪婪匹配:当我们有一个字符串“abc123456”的时候,我们使用”\\d+“的时候,系统每次会自动匹配尽可能多的字符串123456,即贪婪匹配

(2)、解决贪婪匹配:我们用?作用与限定符“+”、“*”上,实现非贪婪匹配,如”\\d+?”,系统每次仅仅会匹配最少的字符串1

4、分组:将字符串分为一个个组别,匹配子字符串

/**
 * 演示分组的使用
 *  1、非命名分组:将一段匹配串分为n个组,然后用group可以分开取出每一个组别
 *  2、命名分组:在组别的开头加上?<名称>,取组时,可以直接用group(组名)来取出相应的组
 */
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegExp05 {
    public static void main(String[] args) {
        String content="aoawijd 1995 2351321 1152 132a 1123";
        String regString1="(\\d\\d)(\\d\\d)";//普通分组
        String regString2="(?<num1>\\d\\d)(?<num2>\\d\\d)";//命名分组
        Pattern pattern = Pattern.compile(regString2);
        Matcher matcher = pattern.matcher(content);
        while(matcher.find()){
            System.out.println("找到: "+matcher.group(0));
            System.out.println("找到第一组: "+matcher.group("num1"));
            //System.out.println("找到第一组: "+matcher.group(1));
            System.out.println("找到第二组: "+matcher.group("num2"));
            //System.out.println("找到第二组: "+matcher.group("2"));
        }
    }
}

5、特别分组(非捕获匹配):不会将分的组保存下来,可以看作是一种特殊的表达式

/**
 *简化表达:如industry|industies--->indus(?:try|ties)
 * content(?:pattern):匹配含有后面多者之一的整个串    例如:venti(?:moyu|qifei) 匹配ventimoyu和ventiqifei两种字符串
 * content(?=pattern):匹配含有后面多者之一的前面的串   例如:venti(?=moyu|qifei) 匹配ventimoyu和ventiqifei两种字符串中的venti
 * content(?!pattern):匹配不含有后面多者之一的前面的串  例如:venti(?!moyu|qifei) 匹配非ventimoyu和ventiqifei两种字符串中的venti
 */

6、定位符:指定开头与结束字符串的格式

/**
 *   (1)、^:指定起始字符,整个字符串必须以^后面的内容开头  ^[0-9]+[a-z]* --->以至少一个数字开头,后面接任意个小写字母的字符串 123、6aa
 *   (2)、$:指定结束字符,整个字符串必须以$前面的内容结束  ^[0-9]\\-[a-z]+$ ---->以一个数字开头,中间用“-”连接,并以至少一个小写字母结尾 1-a
 *	 (3)、其它的定位符,在平时使用的时候较少,如果想进一步了解,请自行搜索(\\b,\\B)
 */

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 定位符用法演示
 */
public class RegExp01 {
    public static void main(String[] args) {
            String content="韩顺平a教育";
            String regString1="^[\u0391-\uffe5]+a[\u0391-\uffe5]+$";
            String regString2="[\u0391-\uffe5]+$";
        Pattern pattern = Pattern.compile(regString1);
            Matcher matcher = pattern.matcher(content);
            while(matcher.find()){
                System.out.println("找到: "+matcher.group(0));
            }
        }

}

7、反向引用:分组后括号里的字符串被捕获后,可以在这个括号后面被再次使用

常用于匹配:

(1)、多个连续的数字 1111、2222、666666

(2)、个位与千位相同,十位与百位相同的数字 1221、2662

(3)、12321等等有特殊要求的数字

/**
 * 反向引用
 *  内部的反向引用:\\  在正则表达式中使用
 *  外部的反向引用:$   在Pattern或者Matcher中的方法中使用
 */
 
 import java.util.regex.Matcher;
import java.util.regex.Pattern;

 public class RegExp10 {
    public static void main(String[] args) {
        String content3 = "12321-999888777";
        String content4 = "我...我要.....学学学学...编程java!";
        /**
         *  1、\\1可以调用第一个分组,引用第一个分组的规则  (\\d)\\1 -->两个相同的数 (\\d)\\1{4}  --->五个相同的数
         *  2、(\\d)(\\d)\\2\\1 --->  1551 2442格式
         *  3、检索12321-999888777 前面一个五位数,中间一个-,最后连续三个相同的9位数
         *  4、结巴程序:
         *         (1).去掉所有的点:   \.+
         *         (2).去掉所有重复的字:  1使用(.)\\1+匹配任意多个相同的字符  2使用反向引用$1来替换匹配到的内容
         */
        String regString3 = "^(\\d)(\\d)\\d\\2\\1-(\\d)\\3{2}(\\d)\\4{2}(\\d)\\5{2}$";

        /**(1).去掉所有的点*/
        String regString41 = "\\.+";

//        Pattern pattern = Pattern.compile(regString41);
//        Matcher matcher = pattern.matcher(content4);
//        content4 = matcher.replaceAll("");
        //使用一条语句替换
        content4 = Pattern.compile(regString41).matcher(content4).replaceAll("");

        System.out.println("content4= " + content4);

        /**(2).去掉所有重复的字*/
        String regString42="(.)\\1+";

//        Pattern pattern1 = Pattern.compile(regString42);
//        matcher=pattern1.matcher(content4);
//        content4 = matcher.replaceAll("$1");
        //使用一条语句直接替换
        content4 = Pattern.compile(regString42).matcher(content4).replaceAll("$1");
        System.out.println("content4= "+content4);
    }
}

四、正则表达式相关类以及常用方法

1、Pattern类

  • Pattern 类:

    pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

    创建过程

    Pattern pattern = Pattern.compile(regString);

Pattern常用方法:

​ Pattern.compile():传入一个正则表达式参数,创建一个Pattern类对象

​ Pattern.matches():整体匹配,尝试将整个区域与模式匹配。

2、Matcher类

  • Matcher 类:

    Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

    创建过程:

    Matcher matcher = pattern.matcher(content);

Matcher常用方法:

/**
 *   matcher.start():返回找到的字符串的首索引
 *   matcher.end():返回找到的字符串的末尾索引+1
 *   matcher.matches():整体匹配,常用于去检验某个字符串是否满足某种规则
 *   matcher.replaceAll():将字符串中满足一定格式的全部替换
 content4 = Pattern.compile(regString41).matcher(content4).replaceAll(""); 
 */

五、String类中支持正则表达式的的常用方法

在String类中使用正则表达式
功能1、替换功能 repalceAll(String regex,String replacement):将正则表达式所对应格式的字符串进行替代
功能2、判断功能 matches(String regex):判断字符是否满足正则表达式格式,返回判断结果。(注:matches自带定位符^$)
功能3、分割功能 split(String regex):将字符串按照某个间隔符进行分割,分割成字符串数组


/**
 * 在String类中使用正则表达式
 */
public class RegExp11 {
    public static void main(String[] args) {
        /**
         * 功能1、替换功能 repalceAll(String regex,String replacement):将正则表达式所对应格式的字符串进行替代
         * 将下文中JDK1.3和JDK1.4替换为JDK
         */
        String content1="2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发布。" +
                "2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升,与J2SE1.3相比,其多了近62%的类和接口。在这些新特性当中," +
                "还提供了广泛的XML支持、安全套接字(Socket)支持(通过SSL与TLS协议)、全新的I/OAPI、正则表达式、日志与断言。2004年9月30日,J2SE1.5发布" +
                ",成为Java语言发展史上的又一里程碑。为了表示该版本的重要性," +
                "J2SE 1.5更名为Java SE 5.0(内部版本号1.5.0),代号为“Tiger”,Tiger包含了从1996年发布1.0版本以来的最重大的更新," +
                "其中包括泛型支持、基本类型的自动装箱、改进的循环、枚举类型、格式化I/O及可变参数。";
        content1 = content1.replaceAll("JDK1\\.(?:3|4)", "JDK");
        System.out.println(content1);
        /**
         * 功能2、判断功能 matches(String regex):判断字符是否满足正则表达式格式,返回判断结果
         * 验证一个手机号,要求必须以138|139开头的 11位数字
         */
        String content2="13988889999";
        if (content2.matches("^13(?:8|9)\\d{8}$")) {
            System.out.println("满足要求");
        }
        else {
            System.out.println("不满足要求");
        }
        /**
         * 功能3、分割功能 split(String regex):将字符串进行分割,分割成字符串数组
         * 要求按照# 或者- 或者~ 或者数字 来分割字符串
         */
        System.out.println("==========");
        String content3="hello#abc-jack12smith~北京";
        String[] split = content3.split("#|-|~|\\d+");
        for (String s :
                split) {
            System.out.println(s);
        }
    }
}

六、正则表达式应用

/**
 * 正则表达式的综合应用
 *  \\d:匹配数字0-9
 *  \\w:匹配字母以及数字和下划线
 *  \\s:匹配非空字符(包括空格、换行符)
 *  \\un:匹配n,其中n为四位数字的unicode字符
 *  汉字匹配:[\u0391-\uffe5]+$
 *  ^:指定起始字符
 *  $:指定结束字符
 */
验证是否为汉字  
验证为邮政编码
验证为qq号码
验证手机号码
验证复杂的url
/**
         * 1、验证电子邮件是否合法:
         *      (1)、只能有一个@
         *      (2)、@前面是用户名,可以是a-z A-Z 0-9 _-字符
         *      (3)、@后面是域名,并且域名只能是英文字母,比如sohu.com 或者tsinghua.org.cn
         */
    	String content1="29-_2asd149@tsinghua.org.cn";
        String regStr1="^[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+$";
        //注:matches方法为整体匹配,不用添加^$定位符

         /**
         * 2、验证是不是整数或者小数
         *  注:此题要考虑正数和负数
         *  如:123 -345 34.89 -87.9 -0.01 0.45等
         *  特殊情况如:  0开头:    0.89
         *             非零数字开头   0023.9--->23.9
         *             正号开头:    +1.36
         */
    	String content2="+0.23";
        String regStr2="^[+-]?([1-9]\\d*|0)(\\.\\d+)?$";
         /**
         * 3、对一个url进行解析
         * 如:http://www.sohu.com:8080/abc/index.html
         *  (1)、要求得到协议是什么  http
         *  (2)、域名  www.sohu.com
         *  (3)、端口是什么 8080
         *  (4)、文件名是什么 index.html
         */
    //法一:分割字符串得到
        String content3="http://www.sohu.com:8080/abc/index.html";
        String regStr3="(://)|:|/(.+/)?";
        String[] split = content3.split(regStr3);
        for (String str :
                split) {
            System.out.println(str);
        }
        //法二:分组得到
        System.out.println("========");
        String regStr31="^([a-zA-Z]+)://([a-zA-Z.]+):(\\d+)[\\w-/]*/([a-zA-Z.]+)$";
        Matcher matcher = Pattern.compile(regStr31).matcher(content3);


import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegExp08_example {
    public static void main(String[] args) {
        String content="www.bilibili.com/video/BV11R4y1L7Gk?spm_id_from=333.851.b_7265636f6d6d656e64.3";
        //验证是否为汉字
        String regString1="^[\u0391-\uffe5]+$";

        //验证为邮政编码
        //要求:是1-9开头的一个六位数,如:12890
        String regString2="^[1-9]\\d{5}$";

        //验证为qq号码
        //要求:是一个1-9开头的一个(5-10)位数
        String regString3="^[1-9]\\d{4,9}$";

        //验证手机号码
        //要求:必须以13,14,15,18 开头的11位数字
        String regString4="^1[3458]\\d{9}$";//注意:[]匹配一个字符,不能写[13|14|15|18],括号中仅能表示一个,如果要多加得用限定符

        //验证复杂的url
        //先确定url的开头部分 https:// |http://
        //通过([\\w-]+\\.)+[\\w-]+  来匹配www.baidu.com
        //通过(\\/[\w-#?=&/%.]*)?$  来匹配最后的字符,注意:[]中的内容全部均不用转意
        String regString5="^((https?)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-#?=&/%.]*)?$";

        Pattern pattern = Pattern.compile(regString5);
        Matcher matcher = pattern.matcher(content);
        if(matcher.find()){
            System.out.println("匹配成功");
        }
        else {
            System.out.println("匹配失败");
        }

        //这里如果使用pattern的matches 整体匹配 更加简洁,自动从头开始检验,省略了^$,而find方法不是
        System.out.println(Pattern.matches(regString5,content));
    }

}
本文标题:JAVASE 正则表达式
文章作者:windpo
发布时间:2022-05-10
原始链接:https://windpo.top/blog/base-java-regexp
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。