Java速通9:正则表达式

185 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

1.基本介绍

推荐一个正则表达式可视化网站:正则表达式可视化

image.png

2.单字符匹配

10-1.png

3.预定义字符

  1. 以一个反斜杠(\)开头,大部分为转义字符。
  2. 在java中,为了让正则表达式能完整地表示预定义字符,需要以两个反斜杠开头(\\ 就表示普通斜杠)

10-2.png

String str = "123";
String regex = "\\d[a-zA-Z]";
String regex2 = "\\.";  // 用来匹配点(.)

str.matches(regex);   // 匹配

4.量词

10-3.png

String regex = "6{3}";
"6".matches(regex);    // false
"66".matches(regex);   // false
"666".matches(regex);  // true


String regex = "6+";
"".matches(regex);    // false
"6".matches(regex);   // true
"66".matches(regex);  // true

5.捕获组

"dog{3}"   匹配  doggg(do开头,g出现3次)
"[dog]{3}" 匹配  匹配3个字符,每个字符都可能是d 或o 或g(如:ggg、dod)
"[abc]{3}": 相当于"[abc][abc][abc]"
    
// 捕获组: 小括号中的内容作为一个整体,即一组
"(dog){3}"    匹配  dogdogdog (dog出现3次)
"(d|o|g){3}"  相当于 "[dog]{3}"
    
// 捕获组 - 反向引用
// 首先要明白,每个捕获组都是有编号的,下标从1开始
// 可通过 \+组编号 来引用组的内容。
"(\d\d)\1"  (\d\d)匹配两位的数字, \1可理解为提取第一组匹配到的数据
            可匹配:121233335757
"([a-z]{2})([A-Z]{2})\2\1"  可匹配:abCECEab

6.边界匹配符

10-4.png

一些概念:

  • 终止符:\r(回车符)、\n(换行符)、\r\n (回车换行)

  • 输入:整个字符串

  • 一行:以终止符结束的字符串片段,或整个输入的结尾的字符串片段。

    (若输入是”dog\ndog\rdog“,那么3个dog各是一行)

  • 边界匹配符,匹配的是位置,不是字符。

  • 什么算是单词?:英文字母,数字,下划线,其他国家的文字,这些东西组合成单词。

  • 什么是单词边界?:单词以外的东西,都是单词边界。

  • 边界匹配符:

10-5.png

// 表示dog左边是单词边界(\b),右边不能是单词边界(\B)
String regex = "\\bdog\\B";

findAll(regex, "This is a doggie."); // dog
findAll(regex, "This is a dog.");    // 匹配不到(dog右边有单词边界)
findAll(regex, "I love adoggie, pig."); // 匹配不到(dog左边无单词边界)





// 表示dog左边是一行的开头(^), 右边是一行的结尾($)
String regex1 = "^dog$";

findAll(regex1, "dog");   // dog
findAll(regex1, "  dog"); // 匹配不到(d的左边不是一行的开头)
findAll(regex1, "dog  "); // 匹配不到(g的右边不是一行的结尾)





// 表示dog左边是输入的开头(A),右边是输入的结尾(z)
String regex2 = "\\Adog\\z";

findAll(regex2, "dog");      // dog
findAll(regex2, "dog\n");    // 匹配不到(dog右边有终结符)
findAll(regex2, "dog\ndog"); // 匹配不到(第二个dog右边是输入的结尾,但左边不是输入的开头)

7.举例

手机号码:"^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$"
邮政编码:"[1-9]\d{5}(?!\d)" (中国邮政编码为6位数字)
腾讯QQ号:"[1-9][0-9]{4,}"   (腾讯QQ号从10000开始)
IP地址:"\d+\.\d+\.\d+\.\d+"

8.Pattern、Matcher

  1. String的matches方法:底层用到了Pattern、Matcher两个类。

  2. 如何在字符串中找到符合要求的字串呢?matcher.find()

    String input = "123_456_78_345";
    String regex = "\\d{3}";  //至于为什么是两个\, 看3.预定义字符
    
    // matches() 要求整个字符串 要符合 regex
    input.matches(regex);    // false
    
    Pattern p = Pattern.compile(regex); // 编译正则式
    Matcher matcher = p.matcher(input); // 将字符串放入比较器
    
    // matcher.matches();  // false
    matcher.find();     // true  在字符串中 查找符合regex 的子串
    matcher.start();    // 返回上次匹配成功的开始索引 0
    matcher.end();      // 返回上次匹配成功的结束索引 3
    matcher.group();    // 返回上次匹配成功的 input 子序列 123
    
  3. 获得 匹配到的所有字串

    String input = "123_456_78_345";
    String regex = "\\d{3}";
    
    Pattern p = Pattern.compile(regex); // 编译正则式
    Matcher matcher = p.matcher(input); // 将字符串放入比较器
    
    while(matcher.find() != false){
        System.out.println(matcher.group());
    }
    
    
    
    
    //封装成方法
    public static void findAll(String regex, String input){
        if(regex == null || input == null){
            return;
        }
    
        Pattern p = Pattern.compile(regex); // 编译正则式
        Matcher matcher = p.matcher(input); // 将字符串放入比较器
    
        boolean found = false; // false 表示没找到
    
        while (matcher.find() != false){
            found = true;
            System.out.format("\"%s\", [%d, %d)%n", matcher.group(), matcher.start(), matcher.end());
        }
    
        if(found == false){
            System.out.println("No match.");
        }
    }
    
  4. 贪婪、勉强、独占的区别

    1. 贪婪:先吞掉整个输入的字符串,若整个串能完全匹配正则式,就输出此串。若不能完全匹配,吐出此串的最后一个字符,接着匹配。能匹配就输出,不能匹配接着删除新串的最后一个字符,如此循环。(从后往前匹配,保证了字串的最大化输出)
    2. 勉强:先吞掉 输入字符串 的第一个字符,判断是否能与正则式匹配。若匹配,输出匹配的内容,并从此字符的下一个字符开始,继续匹配。若不匹配,吞掉第二个字符,再判断是否与正则式匹配。如此循环,直到输入串全部吞进去。
    3. 独占:吞掉整个输入的字符串,若整个串能完全匹配正则式,就输出此串。否则就结束匹配。
    String input = "afooaaafooaa";
    findAll(".*foo", input);   // 贪婪 "afooaaafoo"
    findAll(".*?foo", input);  // 勉强 ["afoo", "aaafoo"]
    findAll(".*+foo", input);  // 独占 匹配失败
    

9.String类与正则式

String类中接收正则表达式 作为参数的 常用方法有:

public String replaceAll(String regex, String replacement) {}
public String replaceFirst(String regex, String replacement) {}
public String[] split(String regex) {}
public boolean matches(String regex) {}