Java正则表达式中的奇怪行为

时间:2017-08-24 19:01:24

标签: java regex

以下代码未在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错过了!

对此行为有任何解释吗?我错过了一些明显的东西吗?

提前致谢。

3 个答案:

答案 0 :(得分:3)

最后的\s+在模式开头与\s+冲突。它们是贪婪的,这意味着它匹配单词MOVE,在它的左边没有留下空格,这意味着它不匹配。

\s+更改为\s+?MOVE匹配。但要注意,这意味着您要求所有捕获的组拥有自己的1个或多个空白字符。单词边界或外观可以解决这个问题。

答案 1 :(得分:2)

您可以使用\s+ Word Boundaries来匹配组中的任何字词,而不是使用\b,因此您的正则表达式应如下所示:

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

<强>输出

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

答案 2 :(得分:1)

要打印整个匹配而不是myStr.substring(m.start(), m.end()),您可以使用m.group(0)m.group()(两者都是相同的,因为group()返回group(0)的结果。同样要查看整个匹配,请使用[ ]等字符围绕它(就像您对组(1)所做的那样)。

所以而不是

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

使用

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

您将看到匹配的内容是[ ELSE ][ COMPUTE ]。如您所见,您在搜索到的令牌后也匹配所有可能的空格。但是,由于您的正则表达式要求匹配,因此至少有一个空格[MOVE ]无法匹配,因为没有剩余不匹配的空格。要解决该问题,您可以使用lookaround mechanism zero-length(它不会消耗匹配的部分)。

因此,您可以将其重写为

而不是\\s+(...)\\s+
(?<=\\s)(...)(?=\\s)

但问题是你的令牌也需要被空格包围,所以你将无法找到放置在字符串开头或结尾的匹配。

其中一个解决方案可能是\b word boundary。它代表 place ,它是字符串的开头/结尾,或者放在[a-zA-Z0-9_]和任何非[a-zA-Z0-9_]字符之间,但这也代表字母字符和-所以,如果您有IF-ELSE,即使您希望将其视为不符合任何描述的单个令牌,它也会单独找到IFELSE(...)部分代币中。

其他解决方案是接受空格,接受由^$表示的字符串的开头和结尾(更多信息位于:http://www.regular-expressions.info/anchors.html)。在这种情况下,您的解决方案可能看起来像

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

BTW通常我们会尽量避免写(A|AB)的情况,因为如果A 足够来匹配整个正则表达式(取决于正则表达式的其余部分){ {1}}将不会被测试。因此,如果您有AB等正则表达式,那么对于字符串(A|AB),您会发现两个匹配项AABA,而不是AA }。这就是为什么我们通常会尝试将其从最具体到更不具体的内容写成AB(或者在文字的情况下,您可以尝试根据它们的长度对它们进行排序)。