在JDK 1.4中,Java增加了对正则表达式的支持。

java与正则相关的工具主要在java.util.regex包中;此包中主要有两个类:PatternMatcher


Matcher 

声明:public final class Matcher extends Objectimplements MatchResult

Matcher 类有final 修饰,可知他不能被子类继承。

含义:匹配器类,通过解释 Pattern 对 character sequence 执行匹配操作的引擎。

注意:此类的实例用于多个并发线程是不安全的。


字段:

    /**
* The Pattern object that created this Matcher.创建此对象的模式匹配。
*/
Pattern parentPattern; /**
* The storage used by groups. They may contain invalid values if
* a group was skipped during the matching.组使用的存储。如果在匹配过程中跳过一个组,它们可能包含无效的值。
*/
int[] groups; /**
* The range within the sequence that is to be matched. Anchors
* will match at these "hard" boundaries. Changing the region
* changes these values.要匹配的序列中的范围。
*/
int from, to; /**
* Lookbehind uses this value to ensure that the subexpression
* match ends at the point where the lookbehind was encountered.
*/
int lookbehindTo; /**
* The original string being matched.匹配的目的字符串。
*/
CharSequence text; /**
* Matcher state used by the last node. NOANCHOR is used when a
* match does not have to consume all of the input. ENDANCHOR is
* the mode used for matching all the input. NOANCHOR表示不必匹配所有的输入;ENDANCHOR表示必须匹配所有的输入。
*/
static final int ENDANCHOR = 1;
static final int NOANCHOR = 0;
int acceptMode = NOANCHOR; /**
* The range of string that last matched the pattern. If the last
* match failed then first is -1; last initially holds 0 then it
* holds the index of the end of the last match (which is where the
* next search starts).最后一个匹配模式的字符串的范围。
*/
int first = -1, last = 0; /**
* The end index of what matched in the last match operation.在最后一次匹配操作中匹配的结束索引。
*/
int oldLast = -1; /**
* The index of the last position appended in a substitution.追加在替换中的最后位置的索引。
*/
int lastAppendPosition = 0; /**
* Storage used by nodes to tell what repetition they are on in
* a pattern, and where groups begin. The nodes themselves are stateless,
* so they rely on this field to hold state during a match.
*/
int[] locals; /**
* Boolean indicating whether or not more input could change
* the results of the last match.
*
* If hitEnd is true, and a match was found, then more input
* might cause a different match to be found.
* If hitEnd is true and a match was not found, then more
* input could cause a match to be found.
* If hitEnd is false and a match was found, then more input
* will not change the match.
* If hitEnd is false and a match was not found, then more
* input will not cause a match to be found.
*/
boolean hitEnd; /**
* Boolean indicating whether or not more input could change
* a positive match into a negative one.
*
* If requireEnd is true, and a match was found, then more
* input could cause the match to be lost.
* If requireEnd is false and a match was found, then more
* input might change the match but the match won't be lost.
* If a match was not found, then requireEnd has no meaning.
*/
boolean requireEnd; /**
* If transparentBounds is true then the boundaries of this
* matcher's region are transparent to lookahead, lookbehind,
* and boundary matching constructs that try to see beyond them.
*/
boolean transparentBounds = false; /**
* If anchoringBounds is true then the boundaries of this
* matcher's region match anchors such as ^ and $.
*/
boolean anchoringBounds = true;

 


构造器

    Matcher() {
}
    Matcher(Pattern parent, CharSequence text) {
this.parentPattern = parent;
this.text = text; // Allocate state storage
int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
groups = new int[parentGroupCount * 2];
locals = new int[parent.localCount]; // Put fields into initial states
reset();
}

构造器有包访问权限,可知不能在包外通过new创建Matcher对象。

如何在自定义的包中得到Matcher类的实例?

Matcher类中没有合适的方法,查阅Pattern类有:

    public Matcher matcher(CharSequence input) {
if (!compiled) {
synchronized(this) {
if (!compiled)
compile();
}
}
Matcher m = new Matcher(this, input);
return m;
}

可知需要通过Pattern对象调用matcher方法来返回Matcher 类的实例。

对照Matcher构造器源码,可知构造器将Pattern对象的引用赋于Matcher中变量parentPattern,目标字符串赋于变量text;并创建了数组groups和locals 。

数组groups是组使用的存储。存储的是当前匹配的各捕获组的first和last信息。

groups[0]存储的是组零的first,groups[1]存储的是组零的last,groups[2]存储的是组1的first,groups[3]存储的是组1的last,依次类推。关于捕获组的信息请看java之Pattern类详解中的组和捕获

初始化后状态表:(具体分析见以下reset()方法)

变量 类型
first  int  -1
last  int  0
oldLast int  -1
lastAppendPosition int  0
from int  0
to int
text.length()
groups
int[]
locals[i] = -1
locals
int[]

locals[i] = -1
parentPattern
Pattern
构造器传入的Pattern对象
text
CharSequence 
构造器传入的目标字符串

部分方法:

1、public String toString()

返回匹配器的字符串表示形式。包含可用于调试的信息的 Matcher 字符串表示形式。未指定确切格式。

源码:

    public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("java.util.regex.Matcher");
sb.append("[pattern=" + pattern());
sb.append(" region=");
sb.append(regionStart() + "," + regionEnd());
sb.append(" lastmatch=");
if ((first >= 0) && (group() != null)) {
sb.append(group());
}
sb.append("]");
return sb.toString();
}

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
System.out.println(m);

打印:

java.util.regex.Matcher[pattern=(\w+)%(\d+) region=0,11 lastmatch=]

2、public Matcher reset()

重置匹配器。

    public Matcher reset() {
first = -1;
last = 0;
oldLast = -1;
for(int i=0; i<groups.length; i++)
groups[i] = -1;
for(int i=0; i<locals.length; i++)
locals[i] = -1;
lastAppendPosition = 0;
from = 0;
to = getTextLength();
return this;
}
   int getTextLength() {
return text.length();
}

可知reset()方法改变了变量first 、last 、oldLast、lastAppendPosition、from、to的值并将数组groups、locals初始化。

状态变化:

变量 类型 新值
first  int  -1
last  int  0
oldLast int  -1
lastAppendPosition int  0
from int  0
to int
text.length()
groups
int[]
locals[i] = -1
locals
int[]

locals[i] = -1

 

测试1:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
if (m.find()) {
System.out.println("开始索引:" + m.start());// 开始索引:0
System.out.println("group():" + m.group());// group():ab%12
}
if (m.find()) {
System.out.println("开始索引:" + m.start());// 开始索引:6
System.out.println("group():" + m.group());// group():cd%34
}

测试2:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
if (m.find()) {
System.out.println("开始索引:" + m.start());// 开始索引:0
System.out.println("group():" + m.group());// group():ab%12
}
m.reset();
if (m.find()) {
System.out.println("开始索引:" + m.start());// 开始索引:0
System.out.println("group():" + m.group());// group():ab%12
}

由测试1和测试2可知reset方法可将Matcher 对象状态初始化。

3、public Matcher reset(CharSequence input)

重置此具有新输入序列的匹配器。

   public Matcher reset(CharSequence input) {
text = input;
return reset();
}

可知此方法在reset()方法的基础上改变了目标字符串的值。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
m.reset("ef%56-gh%78");
while (m.find()) {
System.out.println("group():" + m.group());
}

打印:

group():ef%56
group():gh%78

4、public Pattern pattern()

返回由此匹配器解释的模式。

源码:

    public Pattern pattern() {
return parentPattern;
}

pattern()返回parentPattern,即构造器传入的Pattern对象。

5、public int groupCount()

返回此匹配器模式中的捕获组数。根据惯例,零组表示整个模式。它不包括在此计数中。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
System.out.println(m.groupCount());//

6、public String group()

返回当前查找而获得的与组匹配的所有子串内容。

查看group()源码:

    public String group() {
return group(0);
}

可知group()实际调用了group(int group)方法,参数group为0。组零表示整个模式。

7、public String group(int group)

返回当前查找而获得的与组匹配的所有子串内容。

8、public int start()

返回当前匹配的子串的第一个字符在目标字符串中的索引位置 。

源码:

    public int start() {
if (first < 0)
throw new IllegalStateException("No match available");
return first;
}

可知start()方法返回的是匹配器的状态first。

9、public int start(int group)

返回当前匹配的指定组中的子串的第一个字符在目标字符串中的索引位置 。

10、public int end()

返回当前匹配的子串的最后一个字符的下一个位置在目标字符串中的索引位置 。

源码:

    public int end() {
if (first < 0)
throw new IllegalStateException("No match available");
return last;
}

可知end()方法返回的是匹配器的状态last。

11、public int end(int group)

返回当前匹配的的指定组中的子串的最后一个字符的下一个位置在目标字符串中的索引位置 。

12、public boolean find()

在目标字符串里查找下一个匹配子串。如果匹配成功,则可以通过 startend 和 group 方法获取更多信息。

源码:

    public boolean find() {
int nextSearchIndex = last;
if (nextSearchIndex == first)
nextSearchIndex++; // If next search starts before region, start it at region
if (nextSearchIndex < from)
nextSearchIndex = from; // If next search starts beyond region then it fails
if (nextSearchIndex > to) {
for (int i = 0; i < groups.length; i++)
groups[i] = -1;
return false;
}
return search(nextSearchIndex);
}

从源码中可知nextSearchIndex为下次查找匹配的开始位置;nextSearchIndex的值有三次判定:

1、last==first时,nextSearchIndex++;

2、nextSearchIndex<from时,nextSearchIndex=from;

3、nextSearchIndex>to时,return false;

可通过region(int start,int end)方法修改from和to,以此来影响下次查找匹配的开始位置。

注意:此方法会改变匹配器的状态:first、last和oldLast。

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
while (m.find()) {
System.out.println("group():" + m.group());
System.out.println("start():" + m.start());
System.out.println("end():" + m.end());
System.out.println("group(1):" + m.group(1));
System.out.println("start(1):" + m.start(1));
System.out.println("end(1):" + m.end(1));
System.out.println("group(2):" + m.group(2));
System.out.println("start(2):" + m.start(2));
System.out.println("end(2):" + m.end(2));
System.out.println();
}

打印:

group():ab%12
start():0
end():5
group(1):ab
start(1):0
end(1):2
group(2):12
start(2):3
end(2):5 group():cd%34
start():6
end():11
group(1):cd
start(1):6
end(1):8
group(2):34
start(2):9
end(2):11

可知find()方法匹配了两个子串:ab%12和cd%34;每个子串有2组。

13、public boolean find(int start)

重置此匹配器,然后尝试查找匹配该模式,从指定的位置开始查找下一个匹配的子串。如果匹配成功,则可以通过 startend 和 group 方法获取更多信息。

注意:此方法会改变匹配器的转态。

源码:

    public boolean find(int start) {
int limit = getTextLength();
if ((start < 0) || (start > limit))
throw new IndexOutOfBoundsException("Illegal start index");
reset();
return search(start);
}

从源码可知此方法首先重置匹配器,然后搜索匹配,下次查找匹配的开始位置为指定的start参数。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
if (m.find(1)) {
System.out.println("开始索引:" + m.start());// 开始索引:1
System.out.println("group():" + m.group());// group():b%12
}
if (m.find(0)) {
System.out.println("开始索引:" + m.start());// 开始索引:0
System.out.println("group():" + m.group());// group():ab%12
}
if (m.find()) {
System.out.println("开始索引:" + m.start());// 开始索引:6
System.out.println("group():" + m.group());// group():cd%34
}

当有m.find(1)时,重置匹配器,从索引1处开始匹配,匹配的子串为“b%12”;

当有m.find(0)时,重置匹配器,从索引0处开始匹配,匹配的子串为“ab%12”;

当有m.find()时,并没有重置匹配器,从索引6处开始匹配,匹配的子串为“cd%34”;

14、public int regionStart()

报告此匹配器区域的开始索引。

源码:

    public int regionStart() {
return from;
}

可知end()方法返回的是匹配器的状态from。

15、public int regionEnd()

报告此匹配器区域的结束索引(不包括)。

源码:

    public int regionEnd() {
return to;
}

可知end()方法返回的是匹配器的状态to。

16、public Matcher region(int start,int end)

设置此匹配器的区域限制。重置匹配器,然后设置区域,使其从 start 参数指定的索引开始,到 end 参数指定的索引结束(不包括end索引处的字符)。

    public Matcher region(int start, int end) {
if ((start < 0) || (start > getTextLength()))
throw new IndexOutOfBoundsException("start");
if ((end < 0) || (end > getTextLength()))
throw new IndexOutOfBoundsException("end");
if (start > end)
throw new IndexOutOfBoundsException("start > end");
reset();
from = start;
to = end;
return this;
}

从源代码中可知region方法首先调用reset()重置,然后对from 和to赋值,来设置匹配的目的字符串的范围。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
m.region(0, 4);
while (m.find()) {
System.out.println("group():" + m.group());
System.out.println("regionStart():" + m.regionStart());
System.out.println("regionEnd():" + m.regionEnd());
}

打印:

group():ab%1
regionStart():0
regionEnd():4

17、public boolean lookingAt()
从目标字符串开始位置进行匹配。只有在有匹配匹配的某一子串中包含目标字符串第一个字符的情况下才会返回true。

源码:

    public boolean lookingAt() {
return match(from, NOANCHOR);
}

从源码中可知下次查找匹配的开始位置为from,可通过region(int start,int end)方法修改from的值。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
System.out.println(m.lookingAt());// true
m = p.matcher("%ab%12-cd%34");
System.out.println(m.lookingAt());// false

18、public boolean matches()
只有完全匹配时才会返回true。

源码:

    public boolean matches() {
return match(from, ENDANCHOR);
}

对比上一个方法lookingAt(),从代码上看差别很小,调用的match方法只有一个参数不一样;lookingAt中使用的NOANCHOR,而matches中使用的ENDANCHOR。

NOANCHOR表示不必匹配所有的输入;ENDANCHOR表示必须匹配所有的输入。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("%ab%12");
System.out.println(m.matches());// false
m = p.matcher("ab%12%");
System.out.println(m.matches());// false
m = p.matcher("ab%12");
System.out.println(m.matches());// true

19、public Matcher appendReplacement(StringBuffer sb, String replacement)

将当前匹配子串替换为指定字符串,并将从上次匹配结束后到本次匹配结束后之间的字符串添加到一个StringBuffer对象中,最后返回其字符串表示形式。

注意:对于最后一次匹配,其后的字符串并没有添加入StringBuffer对象中,若需要这部分的内容需要使用appendTail方法

20、public StringBuffer appendTail(StringBuffer sb)
将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。

源码:

    public StringBuffer appendTail(StringBuffer sb) {
sb.append(getSubSequence(lastAppendPosition, getTextLength()).toString());
return sb;
}

查看源码有getSubSequence(lastAppendPosition, getTextLength()),即获取从lastAppendPosition索引处开始,到目的字符串结束索引处之间的子串。

lastAppendPosition为何值?

查阅Matcher类代码后,发现appendReplacement方法中有:

    lastAppendPosition = last;

last即目前最后一次匹配结束后的索引。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("前ab%12中cd%34后");
StringBuffer s = new StringBuffer();
while (m.find()) {
m.appendReplacement(s, "app");
}
System.out.println(s);// 前app中app
m.appendTail(s);
System.out.println(s);// 前app中app后

21、public StringreplaceAll(String replacement)

将匹配的子串用指定的字符串替换。

    public String replaceAll(String replacement) {
reset();
boolean result = find();
if (result) {
StringBuffer sb = new StringBuffer();
do {
appendReplacement(sb, replacement);
result = find();
} while (result);
appendTail(sb);
return sb.toString();
}
return text.toString();
}

查看源码可知此方法首先重置匹配器,然后判断是否有匹配,若有,则创建StringBuffer 对象,然后循环调用appendReplacement方法进行替换,最后调用 appendTail方法并返回StringBuffer 对象的字符串形式。

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
StringBuffer s = new StringBuffer();
System.out.println(m.replaceAll("app"));// app-app

22、public String replaceFirst(String replacement)

将匹配的第一个子串用指定的字符串替换。

    public String replaceFirst(String replacement) {
if (replacement == null)
throw new NullPointerException("replacement");
StringBuffer sb = new StringBuffer();
reset();
if (find())
appendReplacement(sb, replacement);
appendTail(sb);
return sb.toString();
}

查看源码可知此方法其实是 replaceAll方法的减配版本,只对第一次匹配做了替换。

测试:

        Pattern p = Pattern.compile("(\\w+)%(\\d+)");
Matcher m = p.matcher("ab%12-cd%34");
StringBuffer s = new StringBuffer();
System.out.println(m.replaceFirst("app"));// app-cd%34

23、public Matcher usePattern(Pattern newPattern)

更改匹配器的匹配模式。

测试:

    public static void main(String[] args) {
Pattern p = Pattern.compile("[a-z]+");
Matcher m = p.matcher("111aaa222");
System.out.println(piPei(m));// (模式[a-z]+):匹配子串:aaa;开始位置:3;结束位置:6;
m.usePattern(Pattern.compile("\\d+"));
System.out.println(piPei(m));// (模式\d+):匹配子串:222;开始位置:6;结束位置:9;
} public static String piPei(Matcher m) {
StringBuffer s = new StringBuffer();
while (m.find()) {
s.append("匹配子串:" + m.group() + ";");
s.append("开始位置:" + m.start() + ";");
s.append("结束位置:" + m.end() + ";");
}
if (s.length() == 0) {
s.append("没有匹配到!");
}
s.insert(0, "(模式" + m.pattern().pattern() + "):");
return s.toString();
}

可以看到更改匹配模式为"\\d+"后,只匹配到了"222",若需要匹配所有数字字符,应对匹配器初始化。

        Pattern p = Pattern.compile("[a-z]+");
Matcher m = p.matcher("111aaa222");
System.out.println(piPei(m));// (模式[a-z]+):匹配子串:aaa;开始位置:3;结束位置:6;
m.usePattern(Pattern.compile("\\d+"));
m.reset();
System.out.println(piPei(m));// (模式\d+):匹配子串:111;开始位置:0;结束位置:3;匹配子串:222;开始位置:6;结束位置:9;

更多与正则表达式相关内容:

java正则规则表

java正则表达式之Greedy、Reluctant和Possessive

java之Pattern类详解

java之Matcher类详解的更多相关文章

  1. java之StringBuffer类详解

    StringBuffer 线程安全的可变字符序列. StringBuffer源码分析(JDK1.6): public final class StringBuffer extends Abstract ...

  2. java之AbstractStringBuilder类详解

    目录 AbstractStringBuilder类 字段 构造器 方法   public abstract String toString() 扩充容量 void  expandCapacity(in ...

  3. java之StringBuilder类详解

    StringBuilder 非线程安全的可变字符序列 .该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍).如果可能,建议优先采用该类,因为在 ...

  4. java.lang.Thread类详解

    java.lang.Thread类详解 一.前言 位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前 ...

  5. Java中dimension类详解

    Java中dimension类详解 https://blog.csdn.net/hrw1234567890/article/details/81217788

  6. java的ReentrantLock类详解

    ReentrantLock 能用于更精细化的加锁的Java类, 通过它能更清楚了解Java的锁机制 ReentrantLock 类的集成关系有点复杂, 既有内部类, 还有多重继承关系 类的定义 pub ...

  7. JAVA正则表达式:Pattern类与Matcher类详解(转)

    java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表 ...

  8. JAVA正则表达式:Pattern类与Matcher类详解

    java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表 ...

  9. [转] JAVA正则表达式:Pattern类与Matcher类详解(转)

    java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和 Matcher Pattern 一个Pattern是一个正则表达式经编译后的 ...

随机推荐

  1. Springboot Actuator之十二:actuator aop

    前言spring 中aop是一个核心概念,spring boot 是如何实现自动化配置的?现在我们就来分析一下 解析spring boot 中自动化配置是读取/META-INF/spring.fact ...

  2. Spring Security 解析(四) ——短信登录开发

    Spring Security 解析(四) -- 短信登录开发   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security ...

  3. Linux学习笔记之rsync配置

    0x00 rsync 简介 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件,也可以使用 Rsync 同步本地硬盘中的不同目录. ...

  4. Python进阶----pymysql模块的使用,单表查询

    Python进阶----pymysql模块的使用,单表查询 一丶使用pymysql ​   ​   1.下载pymysql包: pip3 install pymysql ​​   ​   2.编写代码 ...

  5. HTML不换行,多余用省略号代替

    最主要的css样式: white-space: nowrap; overflow: hidden; text-overflow: ellipsis; 例子: <!DOCTYPE html> ...

  6. 英语bitellos钻石bitellos单词

    大颗粒的钻石叫做bitellos,四大钻石指的就是“摄政王”.“南非之星”.“蓝色希望”和“光明之山”四颗钻石.经过琢磨的钻石光彩夺目.灿烂无比,历来被誉为“宝石之王”,科研领域里大颗粒的钻石叫做bi ...

  7. SpringBoot学习<二>——SpringBoot的默认配置文件application和多环境配置

    一.SpringBoot的默认文件appliction 上一篇文章已经说明,springboot启动会内嵌tomcat,端口也是默认的8080,如果我们想要改变端口如果做呢? 在springboot项 ...

  8. mysql分析sql语句基础工具 -- explain

    分析sql语句 explain explain (sql语句) G; 分析结果: id sql语句编号如果是连接查询,表之间是平等关系,编号相同:如果有子查询,编号递增. select——type 查 ...

  9. 无法打开jetbrains官网

    找到下面hosts文件,用记事本打开,删除关于jetbrain的文字 需要管理员权限才能操作这个文档: 先后输入cmd和 notepad hosts 删除掉关于jetbrains的模块,再次访问htt ...

  10. Matplotlib同时绘制多张图片

    我现在有一组图片,一共100张图片,每张大小是200*200,即imgs.shape=100*200*200*3 (注意通道数在最后一维). 我需要同时绘制这100张图片,而且每张图片需要写上对应的名 ...