懒惰模式下正则表达式Java运行与Regex Buddy运行结果不同的原因

Regex Buddy大家知道,是一个测试正则表达式和辅助解析正则表达式的神器。真是太太好用了。但是在使用的时候发现一个问题。

我在使用Regex Buddy测试下面这个正则表达式的时候与Java运行结果产生了差异。

(?ims)\s*(with\s+(recursive)?\s+\w+\s*\(.*\)\s*as\s*\(.+\)\s*)?(\bselect\b(.+?))?(\bfrom\b(.+?))(\bwhere\b(.+?))?(\bgroup\s+by\b.+?(\bhaving\b.+?)?)?(\border\s+by\b.+?)?

测试例子用的是:

select c.ysxnm from T_HS_YSX_GCSSYSX c where c.cxlxnm = '66' order by c.rowid desc

Regex Buddy中只能匹配到select c.ysxnm from (注意from后面有一个空格)

这个结果也是正确的。因为注意正则表达式中使用的是.+?。这个就是懒惰模式,会尽量少的匹配。因为from是必须要匹配的,所以select后面的.+?没办法,再懒惰也只能往后匹配了。所以select后面的.+?就匹配了 c.ysxnm ,这样就到了from,from后面的也是.+?,它也是懒惰的,能少匹配就少匹配。它一看,哟,后面的where啦,group啦,order啦都不是必须匹配的,因为是?匹配,所以就强迫不了它了。它就懒得只匹配了一个from后面的空格就交差了。所以整个字符串就匹配了“select c.ysxnm from ”。如图所示


但是我在java中使用

Pattern pattern = Pattern.compile("(?ims)\\s*(with\\s+(recursive)?\\s+\\w+\\s*\\(.*\\)\\s*as\\s*\\(.+\\)\\s*)?(\\bselect\\b(.+?))?(\\bfrom\\b(.+?))(\\bwhere\\b(.+?))?(\\bgroup\\s+by\\b.+?(\\bhaving\\b.+?)?)?(\\border\\s+by\\b.+?)?");
        String sql = "select c.ysxnm from T_HS_YSX_GCSSYSX c where c.cxlxnm = '66' order by c.rowid desc";
        StringBuffer strQuery = new StringBuffer(sql);
        Matcher m = pattern.matcher(strQuery);

        if (!m.matches())
        {
            System.out.println("error sql:\n" + sql);
        }
        StringBuffer result = new StringBuffer(strQuery.length() + 100);
        if (m.group(1) != null)
        {
            result.append(m.group(1));
        }
        if (m.group(9) != null)
        {
            result.append("select count(*) from (");
            result.append(m.group(3) != null ? m.group(3) : "");
            result.append(m.group(5));
            result.append(m.group(7) != null ? m.group(7) : "");
            result.append(m.group(9));
            result.append(") CNT_TB_");
        }
        else
        {
            result.append("select count(*) ");
            result.append(m.group(5));
            result.append(m.group(7) != null ? m.group(7) : "");
        }
        System.out.println(result.toString());

按照Regex Buddy的结果,m.group(5)还有m.group(7)都应该是空的,最后得到的result应该是“select count(*)  ”。

但是java的运行结果却是:“select count(*)  from T_HS_YSX_GCSSYSX c ”。

我研究了很久,都开始怀疑人生了。觉着java有bug。但是再一想,不应该呀。这样的bug不会被我才发现呀。java用的人那么多,要有Bug早就发现了。而且从网上查资料,没找到有人说这个是java的bug。

然后我在想肯定是我理解错误了。首先查了一下java的matches函数,原来matches函数是整个匹配,只有整个字符序列完全匹配成功,才返回True,否则返回False。

我再一看Regex Buddy原来是部分匹配的呀。这个功能原来是和java中的find函数是一致的。最终还是自己理解错了,没弄清函数的用法。

那么Regex Buddy怎么匹配给出的整个测试字符串呢。我找了半天没发现Regex Buddy有这个选项。最后还是google给力,让我找到了答案。其中有篇文章是这么说的:

Matching Whole Lines of Text

Often, you want to match complete lines in a text file rather than just the part of the line that satisfies a certain requirement. This is useful if you want to delete entire lines in a search-and-replace in a text editor, or collect entire lines in an information retrieval tool.

To keep this example simple, let's say we want to match lines containing the word "John". The regex John makes it easy enough to locate those lines. But the software will only indicate John as the match, not the entire line containing the word.

The solution is fairly simple. To specify that we need an entire line, we will use the caret and dollar sign and turn on the option to make them match at embedded newlines. In software aimed at working with text files like EditPad Proand PowerGREP, the anchors always match at embedded newlines. To match the parts of the line before and after the match of our original regular expression John, we simply use the dot and the star. Be sure to turn off the option for the dot to match newlines.

The resulting regex is: ^.*John.*$. You can use the same method to expand the match of any regular expression to an entire line, or a block of complete lines. In some cases, such as when using alternation, you will need to group the original regex together using parentheses.


哦,原来是在正则表达式的开始和结束的位置分别加上^和$符号,这样就表示正则表达式要完全匹配整个字符串。

立马测试一下,果真如此。


总结一下:

折腾了半天,原来就是^和$符号带来的影响。这两个符号分别匹配字符串的开始位置和结束位置。Java在使用matches函数的时候应该是在正则表达式的两端默认加上了^和$符号,这样就会完全匹配整个字符串。这样from后面的.+?就没法那么偷懒了,被逼着还要往下匹配是不是整个字符串符合给出的正则表达式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章