正则表达式怎么玩(一)

118 阅读7分钟

最近工作中遇到了文本处理的工作,我们知道正则表达式是文本处理的利器,之前在学习正则表达式的时候,快速过了一遍,没实际上手过,真正用到还真有点手足无措,我将会分为两篇文章去记录自己复习过程的笔记还有踩的坑,视频学习推荐韩顺平老师录制的课程

快速入门

/**
 * 快速入门:
 *   提取文本中的所有英语单词
 */
public class QuickStartRegex {

    public static void main(String[] args) {
        String text = "Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念," +
        "因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程";
        // 1.创建一个 Pattern 对象,相当于一个正则表达式对象
        Pattern pattern = Pattern.compile("[a-zA-z]+");
        // 2.创建一个匹配器对象
        Matcher matcher = pattern.matcher(text);
        // 3.开始循环匹配,这个匹配器对象按照 pattern 到 text 去匹配
        while (matcher.find()) {
            System.out.println(matcher.group(0));
        }

    }
}

从这个 QuickStart 我们可以看到正则表达式对于文本的处理是非常方便的,如果用传统的方法去找文中英语单词,需要一个一个去遍历识别,代码量大。

常见表达式语法

限定符

用于指定其前面的字符和组合项连续出现多少次

符号描述示例解释匹配的输入
*指定字符重复0次或n次(abc)*仅包含任意多个 abc 的字符串,等效于\w*abc、abcdef
+指定字符重复1次或n次m+(abc)*以至少一个 m 开头,后接任意个 abc 的字符串m、mabc、mabcabc
?指定字符重复 0 次或 1 次m+abc?以至少一个 m 开头,后接 ab 或abc 的字符串mab、mabc、mmmmab、mmabc
{n}只能输入 n 个字符[abcd]{3}由 abcd 中字母组成的任意长度为 3 的字符串abc、dbc、adc

选择匹配符

符号描述示例解释匹配的输入
匹配""之前或之后的表达式abcdab或者cd

分组组合

捕获分组

常用分组构造形式说明
(pattern)非命名捕获。捕获匹配的字符串。编号为 0 的第一个捕获是由整个正则表达式模式匹配的文本,其他捕获结果则根据左括号的顺序从 1 开始自动编号。
(?pattern)命名捕获,将匹配的子字符串捕获到一个组名称或编号名称中。用于 name 的字符串不能包括任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如(?'name')

示例:

/**
 * 非命名分组
 */
public class TestRegxGroup {

    public static void main(String[] args) {

        String str =  "fanzibang 1234 fanfan5678zizibangbang";

        String regex = "(\d\d)(\d\d)";

        Pattern pattern = Pattern.compile(regex);

        Matcher matcher = pattern.matcher(str);

        while(matcher.find()) {
            System.out.println("匹配到字符串:" + matcher.group(0));
            System.out.println("第一个分组的内容:" + matcher.group(1));
            System.out.println("第二个分组的内容:" + matcher.group(2));
        }
    }
}
/**
 * 命名分组
 */
public class TestRegexNameGroup {

    public static void main(String[] args) {

        String str =  "fanzibang 1234 fanfan5678zizibangbang";

        String regex = "(?<g1>\d\d)(?<g2>\d\d)";

        Pattern pattern = Pattern.compile(regex);

        Matcher matcher = pattern.matcher(str);

        while(matcher.find()) {
            System.out.println("匹配到字符串:" + matcher.group(0));
            System.out.println("第一个分组的内容:" + matcher.group(1));
            System.out.println("g1分组的内容:" + matcher.group("g1"));
            System.out.println("第二个分组的内容:" + matcher.group(2));
            System.out.println("g2分组的内容:" + matcher.group("g2"));
        }
    }
}

非捕获分组

常用分组构造形式说明
(?:pattern)匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用 "or" 字符 () 组合模式部件的情况很有用。例如, "industr(?:yies)" 是比 "industryindustries" 更经济的表达式。
(?=pattern)它是一个非捕获匹配。例如,"Windows s(?=9598NT2000)" 匹配 "Windows 2000" 中的 "Windows",但不匹配 "Windows 3.1" 中的 "Windows"。
(?!pattern)该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配。例如,"Windows(?!9598NT2000)" 匹配 "Windows 3.1" 中的 "Windows",但不匹配 "Windows 2000" 中的 "Windows"。

反向引用符

圆括号的内容被捕获后,可以在这个括号后被使用,从而写出一个比较实用的匹配模式,这个被称为反向引用,这种引用既可以是在正则表达式内部,也可以是在正则表达式外部,内部反向引用\分组号,外部反向引用 $分组号。

示例:

  1. 要匹配两个连续的相同数字:(\d)\1
  2. 要匹配五个连续的相同数字:(\d)\1{4}
  3. 要匹配个位与千位相同,十位与百位相同的数字 5225、1551:(\d)(\d)\2\1

字符匹配符

符号描述示例解释匹配的输入
[ ]可接收的字符列表[abcd]a、b、c、d中的任意1个字符
[^]不接收的字符列表[^abc]除a、b、c、d之外的任意1个字符,包括数字和符号
-连字符A-Z任意1个大写字母
.匹配除了\n以外的任何字符a..b以a开头,b结尾,中间包括两个任意字符aabb、ajhb、a*#b
\d匹配单个数字字符,相当于[0-9]\d{3}(\d)?包含3个或4个数字的字符串
\D匹配单个非数字字符,相当于[^0-9]\D(\d)*以单个非数字字符后接任意个数字字符a123、B9866
\w匹配单个数字、大小写字母字符,相当于[0-9a-zA-Z]\d{3}\w{4}以3个数字字符开头后接4个数字字母字符串111a3b6、123456a
\W匹配单个非数字、大小写字母字符,相当于[^0-9a-zA-Z]\W+\d{2}至少1个非数字字母字符开头,后接2个数字字符结尾#78、!99

定位符

规定匹配的字符串出现的位置

符号描述示例解释匹配的输入
^指定起始字符^[0-9]+[a-z]*以至少 1 个数字开头,后接任意个小写字母的字符串123、1、68asd
$指定结束字符^[0-9]\-[a-z]+$以 1 个数字开头后接连字符"-",并以至少 1 个小写字母结尾的字符串2-a、123-abc
\b匹配目标字符串的边界abc\b字符串的边界指的是是子串间有空格,或者是目标字符串的结束位置abcdfsadasdabc fghabc
\B匹配目标字符串的非边界abc\B与\b的含义相反abcdfsadasdabc fghabc

更详细的语法规则,我们可以去菜鸟教程查看。

三个常用的类

接下来我们来认识 Java 正则表达式中三个常用的类:Pattern 类、Matcher 类和 PatternSyntaxException,通过上一篇文章的一些例子,我们知道,Pattern 类 和 Matcher 类,Pattern 对象相当于一个正则表达式对象,负责规定匹配规则,Matcher 相当于一个匹配器对象,负责匹配字符串。

Pattern

Pattern 对象是一个正则表达式对象,Pattern 类没有公共构造方法,通过调用其静态方法创建一个 Pattern 对象,该方法接收一个正则表达式作为它的参数,比如:

Pattern pattern = Pattern.compile("[a-zA-z]+");

Matcher

Matcher 类是对输入的字符串进行匹配的引擎,与 Pattern 类一样,Matcher 类也没有公共构造方法,通过调用 Pattern 对象的 matcher 方法获得一个 Matcher 对象。比如:

Matcher matcher = pattern.matcher("112233");

PatternSyntaxException

PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

常用方法

Pattern

方法说明
public static boolean matches(String regex, CharSequence input)判断是否匹配成功。

示例:

/**
 * Pattern.matches 演示
 */
public class TestPatternMatchersMethod {

    public static void main(String[] args) {
        String regex = "\d\d";
        String text = "abc";
        boolean matches = Pattern.matches(regex, text);
        System.out.println("是否匹配成功:" + matches);
    }
}

==========结果=============
是否匹配成功:false

Matcher

方法说明
public int start()返回上一个匹配项的起始索引。
public int start(int group)返回以前匹配操作期间,由给定组所捕获的子序列的初始索引。
public int end()返回最后匹配字符之后的偏移量。
public int end(int group)返回以前匹配操作期间,由给定组所捕获的子序列的最后字符之后的偏移量。
public boolean lookingAt()尝试从区域开头开始的输入序列与该模式匹配。
public boolean find()尝试查找与该模式匹配的输入序列的下一个子序列。
public boolean find(int start)重置此匹配器,然后尝试查找匹配该模式,从指定索引开始的输入序列的下一个子序列。
public boolean matches()尝试将整个区域与模式匹配。
public Matcher appendReplacement(StringBuffer sb, String replacement)执行非末尾追加和替换步骤。
public StringBuffer appendTail(StringBuffer sb)实现终端追加和替换步骤。该方法从输入序列中读取字符,从追加位置开始,并将其追加到给定的字符串缓冲区。该方法用于在一次或多次调用 appendReplacement 方法后调用,以复制输入序列的剩余部分。
public String replaceAll(String replacement)用给定的替换字符串替换输入序列中与模式匹配的每个子序列。
public String replaceFirst(String replacement)用给定的替换字符串替换输入序列中与模式匹配的第一个子序列。
public static String quoteReplacement(String s)返回指定字符串的字面替换字符串。此方法生成的字符串将在匹配器类的 appendReplacement 方法中用作字面替换 s。生成的字符串将与作为字面序列的 s 中的字符序列相匹配。

示例:

/**
 * 演示 Matcher 类的一些常用方法
 */
public class TestMatcherMethods {

    public static void main(String[] args) {
        String text = "hello world!";
        String regex = "([a-z]+)\1";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        while (matcher.find()) {
            System.out.println("=============");
            System.out.println(matcher.start());
            System.out.println(matcher.end());
            System.out.println("substring: " + text.substring(matcher.start(), matcher.end()));
            System.out.println("匹配的字符串:" + matcher.group());
        }

        // 整体匹配方法,校验某个字符串是否满足规则
        System.out.println("整体匹配:" + matcher.matches());
    }
}

========结果==========
2
4
substring: ll
匹配的字符串:ll
整体匹配:false
/**
 * 演示 Matcher 类的一些常用方法
 */
public class TestMatcherMethods02 {

    public static void main(String[] args) {
        String text = "hello word";
        String regex = "word";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);

        System.out.println("被替换的字符串:" + Matcher.quoteReplacement(text));
        System.out.println("appendTail: " + matcher.appendTail(new StringBuffer("你好!")));
        System.out.println("replaceFirst: " + matcher.replaceFirst("fanzibang"));
        
    }
}

========结果==========
被替换的字符串:hello word
appendTail: 你好!hello word
replaceFirst: hello fanzibang