懶惰模式下正則表達式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後面的.+?就沒法那麼偷懶了,被逼着還要往下匹配是不是整個字符串符合給出的正則表達式

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