Java正則表達式經過回溯與先後查找提取標籤中的內容

在說具體的內容以前先來了解一下什麼是向前查找,向後查找與回溯。咱們經過一下小的例子來理解一下。具體的代碼附錄在最後。html

##向前查找 Java中向前查找的正則表達式是(?=),更加具體的就是 要匹配內容的正則(?=匹配的邊界的正則)。來一個具體的例子,好比咱們有一個具體的字符串2016年:雞翅 15元 雞腿 10元 雞爪 5元,如今咱們要取出裏面的價格15 10 5,而不要2016,顯然咱們不能直接\d+來匹配。 咱們很容易發現規律,咱們須要提取單位‘元’前面的數字,可是咱們不想要單位‘元’,怎麼辦,這裏就可使用向前查找了。java

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。 注意:向前匹配和向後匹配除了表達式的一點區別,還有就是它們的位置區別url

##回溯 回溯應該是正則表達式中最重要的內容之一了,回溯就是標記分組而後引用前面的分組。好比咱們有一個字符串:"[attach]7219[/attach]",咱們在標籤開始匹配到了attach,要在標籤結束再一次使用attach標籤怎麼辦了。若是這個還不太明顯,那麼在來一個字符串看一下,"[a*]7219[/a*]"這個字符串咱們想匹配以a開始的標籤中的數字,顯然正則表達是不能這麼寫,由於這麼寫可能會匹配到[ab]7219[/ac],這個時候咱們就須要回溯引用第一個正則表達式匹配到的字符串。就能夠想下面同樣code

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]這兩個標籤。htm

##經過先後查找與回溯提取標籤中的內容 上面已經介紹了先後查找和回溯,下面就介紹一下經過先後查找和回溯來匹配標籤中的內容。假設咱們有下面的html代碼遞歸

<h1>H1<h2>這是和h2中的內容</h2>H1</h1>

如今咱們要提取h2標籤中的內容:"這是和h2中的內容",咱們的正則表達式應該怎麼寫呢?,這裏咱們就使用先後查找和回溯來匹配。首先前後查找匹配,< h2 >,(?<=<(h2)>),而後匹配h2中的內容.* ,連起來(?<=<(h2)>).*,而後經過向前查找加回溯應用匹配h2標籤結束。(?=</\1>)。完整的正則以下:rem

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());
        }
    }

}
相關文章
相關標籤/搜索