Java正则表达式的奇怪行为

3
以下代码无法在myStr变量中找到字符串"MOVE":
```

以下代码无法在myStr变量中找到字符串"MOVE":

```
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {
    public static void main(String[] args) {
       String myStr = "    ELSE  MOVE   EXT-LNGSHRT-AMT-C TO WK-UNSIGNED-LNGSHRT-AMT  COMPUTE WK-SHORT-AMT = EXT-LNGSHRT-AMT-C * -1.";
       String verbsRegex = "\\s+(ACCEPT|ADD|ALTER|CALL|CANCEL|CLOSE|COMPUTE|DELETE|DISPLAY|DIVIDE|ELSE|EXIT|EVALUATE|EXEC|GO|GOBACK|IF|INITIALIZE|INSPECT|INVOKE|MERGE|MOVE|MULTIPLY|OPEN|PERFORM|READ|RELEASE|RETURN|REWRITE|SEARCH|SET|SORT|START|STOP|STRING|SUBTRACT|UNSTRING|WRITE|COPY|CONTINUE|WHEN)\\s+";

       Pattern p = Pattern.compile(verbsRegex);
       Matcher m = p.matcher(myStr);
       System.out.println("------------------------------------");
       while (m.find()) {
           System.out.println(myStr.substring(m.start(),m.end()));
           System.out.println("("+ m.group(1) + ")");
       }
       System.out.println("------------------------------------");
    }
}

如果我将myStr更改为类似于的内容
       String myStr = "   MOVE  ELSE  MOVE   EXT-LNGSHRT-AMT-C TO WK-UNSIGNED-LNGSHRT-AMT  COMPUTE WK-SHORT-AMT = EXT-LNGSHRT-AMT-C * -1.";

Java开始返回MOVE给我。但在这种情况下,ELSE被错过了!

请问有关于这种行为的解释吗?我是否遗漏了什么明显的东西?

提前致谢。


你只捕获“空格后跟随以下任何字符串然后更多的空格”,因此这仅适用于像“MOVE”或“ELSE”这样的文本,但不适用于“MOVE ELSE”,因为ELSE不仅由空格前导。 - gtgaxiola
你总是打印相同的组... - pedromss
3个回答

3
\s+ 在正则表达式末尾与模式开头相冲突。它们是贪婪的,这意味着它匹配到单词 MOVE,留下了它左侧没有空格,因此不能匹配。
将两个\s+都改为\s+?,那么MOVE就会匹配。但要注意这意味着要求所有捕获组具有自己的一个或多个空格字符。可以使用单词边界或环视解决这个问题。

2

不要使用\s+,可以使用\b 单词边界来匹配组中的任何单词,所以你的正则表达式应该看起来像这样:

\\b(ACCEPT|...|WHEN)\\b

输出

------------------------------------
ELSE
(ELSE)
MOVE
(MOVE)
COMPUTE
(COMPUTE)
------------------------------------

1
为了打印整个匹配,而不是使用myStr.substring(m.start(), m.end()),您可以使用m.group(0)m.group()(两者相同,因为group()返回group(0)的结果)。此外,为了查看整个匹配,请将其用字符[]括起来(就像您对group(1)所做的那样)。
因此,不是使用

System.out.println(myStr.substring(m.start(),m.end()));

使用。
System.out.println("["+m.group()+"]");

并且你会发现你正在匹配的是[ ELSE ][ COMPUTE ]。正如你所看到的,你还在匹配搜索标记后所有可能的空格。但由于你的正则表达式需要至少从一个空格开始匹配,因此[MOVE ]无法匹配,因为没有未匹配的空格留给它。为了解决这个问题,您可以使用环视机制,它是零长度的(它不消耗匹配部分)。

因此,您可以将\\s+(...)\\s+重写为

(?<=\\s)(...)(?=\\s)

但问题在于您的令牌也需要被空格包围,因此您将无法找到放置在字符串开头或结尾的匹配项。其中一种解决方案可能是 \ b,它是{{link1:单词边界}}。 它表示位置,可以是字符串的开头/结尾,也可以位于 [a-zA-Z0-9_] 和任何非 [a-zA-Z0-9_] 字符之间,但这也表示字母字符和 - 之间的位置,因此如果您有 IF-ELSE ,即使您希望将其视为不匹配(...)部分令牌中描述的任何令牌的单个令牌,它也会单独查找 IF ELSE

另一种解决方案是除了接受空格之外,还接受字符串的开头和结尾,分别由^$表示(更多信息请参见:http://www.regular-expressions.info/anchors.html)。在这种情况下,您的解决方案可能如下所示:

(?<=\\s|^)(...)(?=\\s|$)

顺便说一句,通常我们会避免编写类似于(A|AB)的情况,因为如果A足以匹配整个正则表达式(取决于其余部分的样式),那么AB将不会被测试。因此,如果您有像(A|AB)这样的正则表达式,那么对于字符串AAB,您将找到两个匹配项,它们将是AA,而不是AAB。这就是为什么我们通常尝试从最具体到不太具体的方式编写它,例如(AB|A)(或在文字字面量的情况下,可以根据其长度进行排序)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接