Java正则表达式通过回溯与前后查找提取标签中的内容

泪湿孤枕 提交于 2019-12-10 05:22:28

在说具体的内容之前先来了解一下什么是向前查找,向后查找与回溯。我们通过一下小的例子来理解一下。具体的代码附录在最后。

##向前查找 Java中向前查找的正则表达式是(?=),更加具体的就是 要匹配内容的正则(?=匹配的边界的正则)。来一个具体的例子,比如我们有一个具体的字符串2016年:鸡翅 15元 鸡腿 10元 鸡爪 5元,现在我们要取出里面的价格15 10 5,而不要2016,显然我们不能直接\d+来匹配。 我们很容易发现规律,我们需要提取单位‘元’前面的数字,但是我们不想要单位‘元’,怎么办,这里就可以使用向前查找了。

content = "2016年:鸡翅 15元 鸡腿 10元 鸡爪 5元";
//正则表示,匹配元前面的数字,以元作为数字的后边界但是不匹配‘元’
regex = "\\d+(?=元)";

匹配的结果就是15 10 5,不包含2016,后面有代码可以自己测试一下。

##向后查找 首先向后查找的正则表达式是(?<=),更加具体的就是*(?<=匹配边界的正则)要匹配内容的正则*,向后查找和向前查找一样,只不过是匹配边界后面的内容。下面还是通过一个具体的例子来说明。比如我有一个url字符串:http://www.freemethod.cn,我想提取出www.freemethod.cn。要真么做了。这里就可以使用向后匹配。当然你说我有一万种方法可以完成这个事情,在这个例子中的确有很多方法可以完成这个工作,这里为了介绍向后查找,所以就使用向后查找的方法吧。当你了解了向后查找,我相信你在很多需要正则表达式的地方使用会更加灵活。

String content = "http://www.freemethod.cn";
//匹配http://然后向后查找所有的字符
String regex = "(?<=http://).*";

这里就是首先匹配到http://然后以它作为边界匹配后面的.*,匹配的结果是www.freemethod.cn。 注意:向前匹配和向后匹配除了表达式的一点区别,还有就是它们的位置区别

##回溯 回溯应该是正则表达式中最重要的内容之一了,回溯就是标记分组然后引用前面的分组。比如我们有一个字符串:"[attach]7219[/attach]",我们在标签开始匹配到了attach,要在标签结束再一次使用attach标签怎么办了。如果这个还不太明显,那么在来一个字符串看一下,"[a*]7219[/a*]"这个字符串我们想匹配以a开始的标签中的数字,显然正则表达是不能这么写,因为这么写可能会匹配到[ab]7219[/ac],这个时候我们就需要回溯引用第一个正则表达式匹配到的字符串。就可以想下面一样

String content = "freemethod[attach]7219[/attach]fremethod[align]left[/align]";
String regex = "\\[(a.*)\\].*\\[/\\1\\]";

正则表达式中的\1就是对第一个小括号中匹配的内容的引用,同理\2就是对第二个小括号匹配的内容的引用,以此类推\3,\4 \5...像上面的表达式就可以匹配到,[attach]7219[/attach]和[align]left[/align]这两个标签。

##通过前后查找与回溯提取标签中的内容 上面已经介绍了前后查找和回溯,下面就介绍一下通过前后查找和回溯来匹配标签中的内容。假设我们有下面的html代码

<h1>H1<h2>这是和h2中的内容</h2>H1</h1>

现在我们要提取h2标签中的内容:"这是和h2中的内容",我们的正则表达式应该怎么写呢?,这里我们就使用前后查找和回溯来匹配。首先先后查找匹配,< h2 >,(?<=<(h2)>),然后匹配h2中的内容.* ,连起来(?<=<(h2)>).*,然后通过向前查找加回溯应用匹配h2标签结束。(?=</\1>)。完整的正则如下:

String content = "<h1>H1<h2>这是和h2中的内容</h2>H1</h1>";
String regex = "(?<=<(h2)>).*(?=</\\1>)";

如果你想挑战一下,可以试尝试计算一下下面的正则表达式的结果:

  public void testTrace()
    {
        String content = "<h1>外部h1开始<h2>这是和h2中的内容<h2>h2中的h2</h2>h2结束</h2><h1>h1中的h1</h1>外部h1结束</h1>";
        String regex = "<h2>.*?</h2>";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*(?=</\\1>)";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*(?=</h\\d>)";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*?(?=</h\\d>)";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*?(?=</\\1>)";
        execute(content, regex);
    }

如果你觉得还不够复杂,可以尝试让字符串的嵌套和递归更加深一点,再一次挑战尝试一下,你会发现在比较复杂的嵌套中使用前后查找和回溯是多么的美妙。

##完整测试代码

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

import org.junit.Test;

public class TagExtractTest {
    
    /**
     * 前后查找回溯结合提取标签中的内容
     */
    @Test
    public void testTagExtract()
    {
//      String content = "iejoiodi[attach]7219[/attach]djiojeio";
//      String regex = "(?<=\\[(attach)\\])\\d*(?=\\[/\\1\\])";
        String content = "<h1>H1<h2>这是和h2中的内容</h2>H1</h1>";
        String regex = "(?<=<(h2)>).*(?=</\\1>)";
        execute(content, regex);
    }
    
    
    /**
     * 向前查找(?=)
     */
    @Test
    public void testLookForward()
    {
        String content = "178$";
        //匹配$然后查找$前面的数字(\\d+)
        String regex = "\\d+(?=\\$)";
        execute(content, regex);
        
        content = "2016年:鸡翅 15元 鸡腿 10元 鸡爪 5元";
        regex = "\\d+(?=元)";
        execute(content, regex);
    }
    
    /**
     * 向后查找(?<=)
     */
    @Test
    public void testLookBehind()
    {
        String content = "http://www.freemethod.cn";
        //匹配http://然后向后查找所有的字符
        String regex = "(?<=http://).*";
//      String content = "$178";
//      String regex = "(?<=\\$)\\d+";
        execute(content, regex);
    }
    
    /**
     * 回溯 \\1 回溯匹配第一个组,\\2回溯匹配第二个分组依次类推
     * 表达式中的一个()表示一个分组
     */
    @Test
    public void testBackTrace()
    {
        String content = "abcfreemethod[attach]7219[/attach]bcadde";
        String regex = "\\[(attach)\\]\\d+\\[/\\1\\]";
        execute(content, regex);
        content = "freemethod[attach]7219[/attach]fremethod[align]left[/align]";
        regex = "\\[(a.*)\\].*\\[/\\1\\]";
        execute(content, regex);
    }
    
    /**
     * 前后查找结合使用
     */
    @Test
    public void testLookAround()
    {
        String content = "[attach]7219[/attach]";
        String regex = "(?<=\\[(attach)\\])\\d*(?=\\[/attach\\])";
        execute(content, regex);
        
    }
    
    @Test
    public void testTrace()
    {
        String content = "<h1>外部h1开始<h2>这是和h2中的内容<h2>h2中的h2</h2>h2结束</h2><h1>h1中的h1</h1>外部h1结束</h1>";
        String regex = "<h2>.*?</h2>";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*(?=</\\1>)";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*(?=</h\\d>)";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*?(?=</h\\d>)";
        execute(content, regex);
        System.out.println("--------------------------");
        regex = "(?<=<(h\\d)>).*?(?=</\\1>)";
        execute(content, regex);
    }
    
    
    private void execute(String content,String regex)
    {
        System.out.println("content:"+content);
        System.out.println("regex:"+regex);
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(content);
        while(matcher.find())
        {
            System.out.println(matcher.group());
        }
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!