正则表达式

目录

  • 一、代码逻辑
  • 二、底层逻辑分析
    • 1.matcher.find()
    • 2.matcher.group(0)
  • 三、正则表达式语法
    • 1.转义符
    • 2.字符匹配符
    • 3.选择匹配符
    • 4.限定符
    • 5.定位符
    • 6.分组
    • 7.反向引用
  • 四、常用的类
    • 1.Pattern类
    • 2.Matcher类
  • 五、应用实例
    • 1.验证输入的字符串是不是汉字
    • 2.验证邮政编码
    • 3.验证QQ号
    • 4.验证手机号码
    • 5.验证URL地址
    • 6.结巴程序
    • 6.String类的替换分割匹配

一、代码逻辑

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.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]存储的
   是其结束位置  周而复始直到文本遍历结束

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.转义符

\\
在我们使用正则表达式去检索某些特殊字符时,需要用到转移符号,负责检索不到
在java的正则表达式中,两个 \\ 代表其他语言中的一个 \

需要用到转移符号的字符有如下字符:
. * + ( ) $ / \ ? [ ] ^ { }
    /**
     * 演示转义字符的使用
     */
    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));
        }
    }

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);
    /**
     * 演示字符匹配符的使用
     */
    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));
        }
    }

3.选择匹配符

符号 含义 示例 解释
| 匹配 | 之前或之后的表达式 ab|cd ab或者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));
        }
    }

4.限定符

用于指定其前面的字符和组合项连续出现多少次
符号 含义 示例 说明 匹配输入
* 指定字符重复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
    /**
     * 限定符演示
     */
    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));
        }
    }

5.定位符

规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置
符号 含义 示例 说明 匹配输入
^ 指定起始字符 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));
        }
    }

7.反向引用

反向引用:圆括号的内容被捕获后,可以在这个括号后被使用
        这种引用既可以是在正则表达式的内部,也可以是在正则表达式的外部
        内部反向引用使用 \\分组号 ,外部反向引用使用 $分组号

举例:
匹配两个连续的相同数字:(\\d)\\1
匹配五个连续的相同数字:(\\d)\\1{4}
匹配个位与千位相同,十位与百位相同的数:(\\d)(\\d)\\2\\1
    /**
     * 找出一段文本中,找出个位与千位相同,十位与百位相同的数
     */
    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.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);
    }

2.Matcher类

正则表达式_第1张图片

五、应用实例

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("不满足格式");
        }
    }

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("不满足格式");
        }
    }

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("不满足格式");
        }
    }

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("不满足格式");
        }
    }

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("不满足格式");
        }
    }

6.结巴程序

例如 我...是...你你..爸爸爸爸...
输出 我是你爸爸
    /**
     * 处理结巴程序
     */
    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);

    }

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. 0-9 ↩︎

  2. 0-9 ↩︎

你可能感兴趣的:(正则表达式)