TL; DR
什么是背后的设计决策Matcher
的API?
背景
Matcher
有我没想到和我不能找到一个很好的理由行为。 该API文档说:
一旦创建,一个匹配器可以被用于执行三种不同类型的匹配操作的:[...]每个方法返回一个布尔指示成功或失败。 关于成功匹配的详细信息可以通过查询匹配的状态下获得。
什么API文档进一步说就是:
匹配器的显式状态最初是未定义的; 在尝试成功的比赛之前查询的任何部分会导致一个IllegalStateException抛出。
例
String s = "foo=23,bar=42";
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
Matcher matcher = p.matcher(s);
System.out.println(matcher.group("foo")); // (1)
System.out.println(matcher.group("bar"));
此代码抛出一个
java.lang.IllegalStateException: No match found
在(1)
为了解决这个问题,就必须调用matches()
带来的或其他方法Matcher
成允许状态group()
以下工作:
String s = "foo=23,bar=42";
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
Matcher matcher = p.matcher(s);
matcher.matches(); // (2)
System.out.println(matcher.group("foo"));
System.out.println(matcher.group("bar"));
添加调用matches()
在(2)
设置Matcher
到适当的状态,以调用group()
问题,可能不是建设性的
为什么这个API这样设计? 当为什么不自动匹配Matcher
是建立与Patter.matcher(String)
?
其实,你误解了文档。 就拿你引述的语句第二看: -
在尝试成功的比赛之前查询的任何部分会导致一个IllegalStateException抛出。
一个匹配可能会抛出IllegalStateException
有关访问matcher.group()
如果没有找到匹配。
所以,你需要使用下面的测试,以实际启动匹配过程: -
- matcher.matches() //Or
- matcher.find()
下面的代码: -
Matcher matcher = pattern.matcher();
只需创建一个matcher
的实例。 事实上这并不会匹配的字符串。 即使有一个成功的匹配。 所以,你需要检查以下条件,以检查成功匹配: -
if (matcher.matches()) {
// Then use `matcher.group()`
}
而如果在条件if
返回false
,这意味着什么也没有匹配。 所以,如果你使用matcher.group()
不检查这种情况下,你会得到IllegalStateException
,如果比赛没有被发现。
假设,如果Matcher
的目的是你说的方式,那么你就必须做一个null
检查,以检查是否找到匹配与否,调用matcher.group()
像这样: -
你的思考方式应该完成的事情: -
// Suppose this returned the matched string
Matcher matcher = pattern.matcher(s);
// Need to check whether there was actually a match
if (matcher != null) { // Prints only the first match
System.out.println(matcher.group());
}
但是,如果你要打印任何进一步的匹配,因为模式可以在一个字符串匹配多次,对于这一点,应该有一个方法来告诉匹配寻找下一个比赛。 但是, null
检查将不能够做到这一点。 对于您将不得不将您的匹配着下一个字符串相匹配。 因此,也有定义的各种方法Matcher
类为服务宗旨。 该matcher.find()
方法,直到所有的比赛中找到匹配字符串。
还有其他的方法也,谓match
以不同的方式串,这取决于你如何想匹配。 因此,它最终对Matcher
类做matching
对字符串。 Pattern
类只创建一个pattern
来匹配。 如果Pattern.matcher()
是到match
的图案,则必须有一些方法来确定不同的方法来match
,因为matching
可以以不同的方式。 因此,有来自的需求Matcher
类。
所以,它的方式居然是: -
Matcher matcher = pattern.matcher(s);
// Finds all the matches until found by moving the `matcher` forward
while(matcher.find()) {
System.out.println(matcher.group());
}
所以,如果有字符串,你的第一个方式发现的4场比赛,将只打印第一位的,而第二个方法将打印所有的比赛,通过移动matcher
前进到下一个模式相匹配。
我希望清楚。
的文档Matcher
类描述了三种方法提供,它说的: -
甲匹配从图案通过调用图案的匹配方法创建的。 一旦创建了匹配可用于执行三种不同的匹配操作:
不幸的是,我一直没能找到任何其他官方消息来源,说明确为什么这个问题如何 。
我的回答是非常相似的罗希特耆那教的,但包括一些原因的“额外”的步骤是必要的。
java.util.regex中实现
该行:
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
导致要分配的新的模式的对象,并且它在内部存储表示RE的结构 - 的信息,如字符,组序列,贪婪与非贪婪,重复等的选择。
这种模式是无状态的不可变的,所以它可重复使用,是多theadable和优化井。
该行:
String s = "foo=23,bar=42";
Matcher matcher = p.matcher(s);
返回一个新的Matcher
为对象Pattern
和String
-一个尚未读取的字符串。 Matcher
实际上只是一个状态机的状态,其中状态机的Pattern
。
匹配可以通过使用下面的API通过匹配过程步进状态机运行:
-
lookingAt()
尝试以匹配输入序列,起始于开始时,对所述图案 -
find()
扫描寻找与模式匹配的下一个子输入序列。
在这两种情况下,中间状态可使用读取start()
end()
和group()
方法。
这种方法的好处
为什么会有人想这样做,通过解析步?
从具有定量大于1(即重复,并最终超过一次匹配多个组)更大的群体获得的值。 例如,在低于该琐碎RE解析变量赋值:
Pattern p = new Pattern("([az]=([0-9]+);)+"); Matcher m = p.matcher("a=1;b=2;x=3;"); m.matches(); System.out.println(m.group(2)); // Only matches value for x ('3') - not the other values
请参见上的“组名”,“组和捕捉”的Javadoc中的部分模式
- 显影剂可以使用RE作为词法分析器和显影剂可以在lexed令牌绑定到解析器 。 在实践中,这将工作简单域语言,但正则表达式可能不是去一个完全成熟的计算机语言的方式。 编辑这部分是与以前原因,但它经常可以更容易,更有效的创建解析树处理文本比第一乐星所有的输入。
- (对于勇敢善良),你可以调试的RE,并找出哪些子未能匹配(或不正确的匹配)。
然而,在大多数场合,你不需要通过匹配步骤的状态机,所以有一个方便的方法( matches
它运行匹配完成模式)。
如果匹配会自动匹配输入字符串,将被浪费的情况下,你想找到的模式工作 。
一个匹配可以用来检查模式matches()
输入字符串,它可以用来find()
输入字符串的模式(甚至多次找到所有匹配的子串)。 直到你调用这两个方法之一,匹配不知道要执行什么样的测试,所以不能给你任何匹配组。 即使你调用这些方法之一,呼叫可能会失败-的格局没有找到-在这种情况下,一个呼叫group
必须也失败。
这是预期和记录。
其原因是, .matches()
返回一个布尔值指示是否有一个匹配。 如果有一个匹配,那么你可以调用.group(...)
有意义。 否则,如果没有匹配,调用.group(...)
是没有意义的。 所以,你不应该被允许调用.group(...)
调用之前matches()
使用匹配的正确方式是类似如下:
Matcher m = p.matcher(s);
if (m.matches()) {
...println(matcher.group("foo"));
...
}
我的猜测是设计决定是基于具有了清晰,明确的语义并没有与之相匹配的性质混为一谈存在疑问。
想想看:你会期望匹配器查询返回如果匹配未成功匹配的东西吗?
让我们先考虑group()
如果我们没有成功匹配的东西,匹配器不应返回空字符串,因为它不匹配空字符串。 我们可以返回null
在这一点上。
好了,现在让我们考虑start()
和end()
。 每个返回int
。 什么int
值是在这种情况下有效吗? 当然,没有正数。 什么负数将是适当的? -1?
考虑到这一切,用户仍然不得不检查返回值的每个查询,以验证是否发生或不匹配。 或者,你可以检查,看它是否成功完全匹配,如果成功,查询语义都具有良好定义的含义。 如果不是这样,用户获得一致的行为,无论哪个角度进行查询。
我会授予该重新使用IllegalStateException
可能不会导致错误情况的最好的说明。 但是,如果我们要重命名/子类IllegalStateException
到NoSuccessfulMatchException
,应该能够体会到当前的设计如何执行查询的一致性,并鼓励用户使用具有已知的要求的时间被定义语义查询。
TL; DR:什么是问一个活的生物体死亡的具体原因的价值?
您需要检查返回值matcher.matches()
它会返回true
当找到匹配, false
,否则。
if (matcher.matches()) {
System.out.println(matcher.group("foo"));
System.out.println(matcher.group("bar"));
}
如果matcher.matches()
不匹配并调用matcher.group(...)
你仍然会得到一个IllegalStateException
。 这正是该文件说:
匹配器的显式状态最初是未定义的; 在尝试成功的比赛之前查询的任何部分会导致一个IllegalStateException抛出。
当matcher.match()
返回false
,没有成功的比赛已经发现,并没有做出很大的意义,通过调用例如,以获取有关比赛的信息group()
文章来源: Rationale for Matcher throwing IllegalStateException when no 'matching' method is called