[疯狂Java]正则表达式:Pattern、Matcher、String对正则表达式的支持

时间:2021-08-02 11:55:39

1. 正则表达式引擎:

    1) 正则表达式其实跟SQL语句一样,其实本质都是查询命令,需要先编译然后再用相应的执行引擎启动并查询;

    2) 为什么正则表达式的本质是命令?

         i. 由于正则表达式中包含很多特殊字符,如*、^等,它们并不属于普通的纯文本字符;

         ii. 它们对于引擎来讲其实是一种特殊的命令(动作),例如*会被解释为“连续0个或多个任意字符”;

         iii. 那么引擎就会把*编译成一条命令(一个函数,底层是flex/bison实现的引擎),该命令的内容是“匹配连续0个或多个任意字符"

         iv. 也就是说正则表达式在编译后会被拆分成(按照子表达式)若干个函数,匹配时会组合这些函数进行匹配!

     3) 因此正则表达式跟SQL语句一样,必须要先经过编译才能执行;


2. Pattern:正则表达式

    1) 该类就表示正则表达式,该类的对象表示一个编译好的正则表达式;

!!其实正则表达式也叫模式串,那它去匹配的串叫做主串,因此正则表达式也可叫做pattern(即模式的意思);

    2) 但是编译是需要手动显式完成的,要利用Pattern的静态工具方法compile来完成:static Pattern Pattern.compile(String regex);

!!Pattern不提供构造器,正则表达式只能通过编译获得!

    3) 编译的另一个好处:其实跟SQL的preparedStatement一样,编译过了以后可以重复使用(已经保存在内存中)无需再编译,加到了执行效率;

    4) Pattern和String一样,是不可变类,即编译后该对象的内容就确定了,无法修改,正因为这种只读的性质,Pattern必然是线程安全的,并发使用没有安全隐患;

    5) 获取Pattern本身的表达式字符串:编译时会把表达式字符串本身regex保存在Pattern对象的内部数据成员pattern中

         i. public String pattern() { return pattern; }

         ii. public String toString() { return pattern; }

!!例如:Pattern p = Pattern.compile("\\w+");  那么p.pattern()得到的就是"\w+"这个模式串本身

!!其中Pattern重载了toString,因此可以很方便的用System的print系列方法直接打印Pattern对象,得到的将是正则表达式本身的字符串;


3. Matcher:正则表达式引擎

    1) 有了Pattern对象以后就可以用Matcher来匹配正则表达式了,其实从Matcher这个名字也可以看出,该类对象就是用来执行匹配动作的,它就是正则表达式引擎;

    2) Matcher包含两个要素,第一就是模式串(正则表达式Pattern),第二就是主串了(就是用模式串去匹配的串):

         i. 也就是说Matcher里包含一个Pattern对象(成员)表示模式串(parentPattern),也必须要包含一个CharSequence对象(成员)表示主串(text);

         ii. 接着调用Matcher的各种对象方法就可以完成匹配、查找等一系列操作了;

         iii. 很可惜Matcher虽然有这样的构造器(Matcher(Pattern parent, CharSequence text);)但是该构造器不可见,不能在外部使用;

    3) 必须使用Pattern对象的matcher方法构造Matcher对象:Matcher Pattern.matcher(CharSequence input);

!!该方法将构造一个Matcher对象并返回,以当前的Pattern对象作为parentPattern成员(模式串),以input作为text成员(主串);

!!例如:Pattern p = Pattern.complie("\\w+"); Matcher m = p.matcher("hello everyone");   等于: Matcher m = new Matcher(Pattern.complie("\\w+"), "hello everyone");

    4) Matcher的常用方法:都是对象方法

         i. 重置:返回的是重置后的Matcher

            a. Matcher reset();  // 由于进行匹配操作的时候需要维护很多状态(比如当前匹配到了什么位置之类的),该操作会清除所有状态,回到开始匹配之前

            b. Matcher reset(CharSequence input);  // 清除状态的同时将主串重设为input

!!很可惜,Matcher可以重设主串,但不能重设模式串,模式串是嵌入到Matcher中的;

         ii. 获取Matcher中嵌入的模式串:Pattern pattern();   // 返回嵌入的Pattern对象


4. 使用Matcher进行匹配操作:都是Matcher的对象方法

    1) 位置指针:由于匹配的过程其实也是扫描主串的过程,因此必须要维护一个位置指针来指示当前扫描到了什么位置;

>>>向下匹配:

    2) boolean find();

         i. 从当前位置向下继续匹配,如果匹配到了一个子串就返回true,如果一直扫描到主串末尾还没有符合要求的子串就返回false;

         ii. 一旦匹配成功就会保存状态,例如保存当前匹配成功的子串;

    3) boolean find(int start);

         i. 从主串的start位置处开始匹配;

         ii. 该方法会重设位置指针,这也就意味着要重新开启一轮新的匹配;

         iii. 因此该方法会默认清楚所有状态,就相当于先reset,然后再从主串的start位置处进行boolean find()操作;

>>>查询匹配到的各种信息:是和find系列配合使用的,一旦find成功,就可以调用下面的方法查询此次匹配所保存的各种信息

    4) int start();  // 返回本次匹配到的子串在主串中的起始位置

    5) int end();  // 返回本次匹配到的子串在主串中的结束位置(最后一个字符的索引+1)

    >>>获得捕获组:

!!这里只是先略微简介一下捕获组的概念:

         a. 起始捕获组就是匹配到的子串,即本次匹配捕获到的匹配内容,这就是捕获的含义,捕获就是匹配的意思;

         b. 正则表达式引擎会将捕获到的(匹配成功的)子串分割成若干组;

         c. 分组的依据就是正则表达式中的括号位置,例如表达式"(a)(b)(c)",该表达式将匹配字符串"abc",那么匹配到的结果会被分成三组(按照括号分),分别是"a"、"b"、"c",会对这些组一次编号,从1开始计,"a"就是组1,"b"是组2,"c"是组3;

    6) String group();  // 返回所有组的连接,即返回整个匹配到的子串

    7) String group(int group);   // 返回第group组的内容

    8) String group(String name);  // 返回名字为name的组的内容

!!Java 7以后支持在正则表达式中对捕获组命名(毕竟用组号来引用捕获组不见名知意,不方便编程,如果捕获组数量庞大,那么数字引用的方式将会非常不友好);

!!具体的命名规则以及正则表达式的语法会在下一章中详细讲解

!!有了捕获组的概念后,start和end也可以查询相应的捕获组的位置信息了:

        a. int start([int group | String name]);

        b. int end([int group | String name]);

!!分别返回指定捕获组的位置信息,如果无参就是返回整个匹配到的子串的位置信息;

    9) 标准的配合find进行匹配的示例:

while (m.find()) {
String s = m.group(); // m.start()、m.end()等
...
}
>>>全局匹配:

    10) boolean matches();   // 必须整个主串匹配才返回true

    11) boolean lookingAt();  // 只要主串的开头匹配就可以返回true,比如用"abc"匹配主串"abcxxx"就行,因为主串前缀能匹配上"abc"


5. String对正则表达式的支持:

    1) 除了基本的正则表达式功能之外,String也提供了正则表达式匹配的支持;

    2) 以下介绍的都是String的对象方法,用于正则表达式相关的操作;

    3) 检查String对象是否完全匹配一个正则表达式:boolean matches(String regex);  // 必须是全局的完全匹配,并不是只要包含就行!

    4) 全局替换:String replaceAll(String regex, String replacement);  // 将所有匹配regex的子串都替换成replacement字符串

    5) 只替换第一个匹配到的子串:String replaceFirst(String regex, String replacement);

    6) 切分:String[] split(String regex);  // 以regex作为分隔符将字符串分割成多个子串