理由匹配器抛出IllegalStateException异常时没有“匹配”方法被调用(Rationa

2019-06-18 13:05发布

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)

Answer 1:

其实,你误解了文档。 就拿你引述的语句第二看: -

在尝试成功的比赛之前查询的任何部分会导致一个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类描述了三种方法提供,它说的: -

甲匹配从图案通过调用图案的匹配方法创建的。 一旦创建了匹配可用于执行三种不同的匹配操作:

  • 比赛方法试图匹配针对图案的整个输入序列。

  • 该方法lookingAt试图匹配输入序列,起始于开始时,对所述图案。

  • find方法扫描寻找与模式匹配的下一个子输入序列。

不幸的是,我一直没能找到任何其他官方消息来源,说明确为什么这个问题如何



Answer 2:

我的回答是非常相似的罗希特耆那教的,但包括一些原因的“额外”的步骤是必要的。

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为对象PatternString -一个尚未读取的字符串。 Matcher实际上只是一个状态机的状态,其中状态机的Pattern

匹配可以通过使用下面的API通过匹配过程步进状态机运行:

  • lookingAt()尝试以匹配输入序列,起始于开始时,对所述图案
  • find()扫描寻找与模式匹配的下一个子输入序列。

在这两种情况下,中间状态可使用读取start() end()group()方法。

这种方法的好处

为什么会有人想这样做,通过解析步?

  1. 从具有定量大于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中的部分模式

  2. 显影剂可以使用RE作为词法分析器和显影剂可以在lexed令牌绑定到解析器 。 在实践中,这将工作简单域语言,但正则表达式可能不是去一个完全成熟的计算机语言的方式。 编辑这部分是与以前原因,但它经常可以更容易,更有效的创建解析树处理文本比第一乐星所有的输入。
  3. (对于勇敢善良),你可以调试的RE,并找出哪些子未能匹配(或不正确的匹配)。

然而,在大多数场合,你不需要通过匹配步骤的状态机,所以有一个方便的方法( matches它运行匹配完成模式)。



Answer 3:

如果匹配会自动匹配输入字符串,将被浪费的情况下,你想找到的模式工作

一个匹配可以用来检查模式matches()输入字符串,它可以用来find()输入字符串的模式(甚至多次找到所有匹配的子串)。 直到你调用这两个方法之一,匹配不知道要执行什么样的测试,所以不能给你任何匹配组。 即使你调用这些方法之一,呼叫可能会失败-的格局没有找到-在这种情况下,一个呼叫group必须也失败。



Answer 4:

这是预期和记录。

其原因是, .matches()返回一个布尔值指示是否有一个匹配。 如果有一个匹配,那么你可以调用.group(...)有意义。 否则,如果没有匹配,调用.group(...)是没有意义的。 所以,你不应该被允许调用.group(...)调用之前matches()

使用匹配的正确方式是类似如下:

Matcher m = p.matcher(s);
if (m.matches()) {
  ...println(matcher.group("foo"));
  ...
}


Answer 5:

我的猜测是设计决定是基于具有了清晰,明确的语义并没有与之相匹配的性质混为一谈存在疑问。

想想看:你会期望匹配器查询返回如果匹配未成功匹配的东西吗?

让我们先考虑group() 如果我们没有成功匹配的东西,匹配器不应返回空字符串,因为它不匹配空字符串。 我们可以返回null在这一点上。

好了,现在让我们考虑start()end() 。 每个返回int 。 什么int值是在这种情况下有效吗? 当然,没有正数。 什么负数将是适当的? -1?

考虑到这一切,用户仍然不得不检查返回值的每个查询,以验证是否发生或不匹配。 或者,你可以检查,看它是否成功完全匹配,如果成功,查询语义都具有良好定义的含义。 如果不是这样,用户获得一致的行为,无论哪个角度进行查询。

我会授予该重新使用IllegalStateException可能不会导致错误情况的最好的说明。 但是,如果我们要重命名/子类IllegalStateExceptionNoSuccessfulMatchException ,应该能够体会到当前的设计如何执行查询的一致性,并鼓励用户使用具有已知的要求的时间被定义语义查询。

TL; DR:什么是问一个活的生物体死亡的具体原因的价值?



Answer 6:

您需要检查返回值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