正则表达式高级用法 (.*?)【原】

开发过程中的真实场景 A报文

开发过程中的真实场景

A报文

<?xml version="1.0" encoding="utf-8"?>
<PACKET>
    <HEAD>
        <SERVICE_NAME>seeYou</SERVICE_NAME>
    </HEAD>
    <BODY>
        <CONTENT>

            <![CDATA[
                 <?xml version="1.0" encoding="utf-8"?>
                 <PACKET>
                 <![CDATA[
                    nice to meet you!
                 ]]>
                </PACKET>
            ]]>

        </CONTENT>
        
    </BODY>
</PACKET>

 

嵌套在A报文中的B报文

<?xml version="1.0" encoding="utf-8"?>
<PACKET>
<![CDATA[
     nice to meet you!
]]>
</PACKET>

 

场景

我得到A报文,后需要解析提取B报文转发给目标系统.

而现有解析代码PatternTool.java如下:

package kingtool;

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

public class PatternTool {

    /**
     * 从regex表达式中提供对应的值
     * @author King
     */
    public static String parsePattern(String regex, String content ,int groupNum) {
        String ret = "";
        String str = "";
        String output ="";
        try {
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(content);
            if (m.find()) {
                for(int i = 0 ; i  <= m.groupCount() ; i ++){
                    if(i == 0){
                        
                    }else{
                        str = m.group(i);
                        output = String.format("解析得正则表达式%s中group(%d)匹配的值\n",regex,i);
                        System.out.println(output);
                        System.out.println(str);
                    }
                }
                ret = m.group(groupNum);
                System.out.println("返回第"+groupNum+"组匹配到的内容:\n"+ret);
            }else{
                System.out.println("未解析到正则表达式"+regex+"匹配的的值\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ret;
    }
    
    public static void main(String[] args) {
        String content = FileTool.readStringFromFile("D://c.txt", "GBK"    );
        //希望匹配<CONTENT>.*<![CDATA[.*]]>.*</CONTENT>
        String regex = "<CONTENT>(.*)<!\\[CDATA\\[(.*)\\]\\]>(.*)</CONTENT>";
        String ret = parsePattern(regex,content,2);
    }
}

 

解析后,打印结果如下(并非最终想要的B报文):

文件 D://c.txt存在与否?: true
读到的文件内容如下:
<?xml version="1.0" encoding="utf-8"?><PACKET>    <HEAD>        <SERVICE_NAME>seeYou</SERVICE_NAME>    </HEAD>    <BODY>        <CONTENT>            <![CDATA[                 <?xml version="1.0" encoding="utf-8"?>                 <PACKET>                 <![CDATA[                    nice to meet you!                 ]]>                </PACKET>            ]]>        </CONTENT>            </BODY></PACKET>
解析得正则表达式<CONTENT>(.*)<!\[CDATA\[(.*)\]\]>(.*)</CONTENT>中group(1)匹配的值

            <![CDATA[                 <?xml version="1.0" encoding="utf-8"?>                 <PACKET>                 
解析得正则表达式<CONTENT>(.*)<!\[CDATA\[(.*)\]\]>(.*)</CONTENT>中group(2)匹配的值

                    nice to meet you!                 ]]>                </PACKET>            
解析得正则表达式<CONTENT>(.*)<!\[CDATA\[(.*)\]\]>(.*)</CONTENT>中group(3)匹配的值

        
返回第3组匹配到的内容:
        

 

现在来分析以上原因:

1.因为正则默认是贪婪的(全取模式,能吃多少是多少),所以第一个(.*)先取 

然后一点一点从尾部吐出来,直到匹配(<!\[CDATA\[(.*)\]\]>)就停止吐字符,至此 group(1) 匹配结束.

 2. 现在来匹配第二个(.*),老规矩先全取

 

然后一个一个吐字符,直到遇到]]>

 

3. 最后来匹配第三个(.*),老规矩先全取

 

直到遇到</CONTENT>停下

 

最终,这完全不是我们希望看到的匹配结果 nice to meet you! ]]> </PACKET>

 

解决方案: 加个?变成懒猫 lazy 模式即可

把PatternTool.java中main主函数的正则匹配式regex中的第一个括号中加一个?,变成懒猫 lazy 模式 ,即  (.*?)

public static void main(String[] args) {
        String content = FileTool.readStringFromFile("D://c.txt", "GBK"    );
        //希望匹配<CONTENT>.*<![CDATA[.*]]>.*</CONTENT>
        String regex = "<CONTENT>(.*?)<!\\[CDATA\\[(.*)\\]\\]>(.*)</CONTENT>";
        String ret = parsePattern(regex,content,2);
    }

解析后,打印结果如下(是最终想要的B报文):

文件 D://c.txt存在与否?: true
读到的文件内容如下:
<?xml version="1.0" encoding="utf-8"?><PACKET>    <HEAD>        <SERVICE_NAME>seeYou</SERVICE_NAME>    </HEAD>    <BODY>        <CONTENT>            <![CDATA[                 <?xml version="1.0" encoding="utf-8"?>                 <PACKET>                 <![CDATA[                    nice to meet you!                 ]]>                </PACKET>            ]]>        </CONTENT>            </BODY></PACKET>
解析得正则表达式<CONTENT>(.*?)<!\[CDATA\[(.*)\]\]>(.*)</CONTENT>中group(1)匹配的值

            
解析得正则表达式<CONTENT>(.*?)<!\[CDATA\[(.*)\]\]>(.*)</CONTENT>中group(2)匹配的值

                 <?xml version="1.0" encoding="utf-8"?>                 <PACKET>                 <![CDATA[                    nice to meet you!                 ]]>                </PACKET>            
解析得正则表达式<CONTENT>(.*?)<!\[CDATA\[(.*)\]\]>(.*)</CONTENT>中group(3)匹配的值

        
返回第2组匹配到的内容:
                 <?xml version="1.0" encoding="utf-8"?>                 <PACKET>                 <![CDATA[                    nice to meet you!                 ]]>                </PACKET>            

 

现在来分析以上原因:

1.因为加了?后,正则变成了懒猫 lazy 模式(特别地懒,能不吃就不吃),所以第一个(.*?)先取 

然后一点一点吃字符进来,直到匹配(<!\[CDATA\[(.*)\]\]>)就停止吃字符,至此 group(1) 匹配结束.

 2. 现在来匹配第二个(.*),老规矩先全取

 

然后一个一个吐字符,直到遇到]]>

 

3. 最后来匹配第三个(.*),老规矩先全取

 

直到遇到</CONTENT>停下

 

最终,这就是我们希望看到的匹配结果 :

<?xml version="1.0" encoding="utf-8"?>
                 <PACKET>
                 <![CDATA[
                    nice to meet you!
                 ]]>
                </PACKET>

 

 

 

 

 

总结:

正则默认贪婪 greedy 模式 : 能取多少是多少

加了?后变成懒猫 lazy 模式 : 能不取就尽量不取

 

 

本文原创,请转明出处,by 金墨痴.