簡體   English   中英

Java正則表達式向后引用兩位數

[英]Java regex backreference for two digits

我正在使用正則表達式,並且想在Java中String類的replaceAll方法上使用它。

我的正則表達式可以正常工作,並且groupCount()返回11。因此,當我嘗試使用指向第11個組的后向引用替換文本時,我得到的第一個組帶有附加的“ 1”,而不是第11個組。

String regex = "(>[^<]*?)((\+?\d{1,4}[ \t\f\-\.](\d[ \t\f\-\.])?)?(\(\d{1,4}([\s-]\d{1,4})?\)[\.\- \t\f])?((\d{2,6}[\.\- \t\f])+\d{2,6})|(\d{6,16})([;,\.]{1,3}\d{3,}#?)?)([^<]*<)";
String text = "<span style=\"font-size:11.0pt\">675-441-3144;;;78888464#<o:p></o:p></span>":
String replacement = text.replaceAll(regex, $1<a href="tel:$2">$2</a>$11");

我期望得到以下結果:

<span style=\"font-size:11.0pt\"><a href=\"tel:675-441-3144;;;78888464#\">675-441-3144;;;78888464#</a><o:p></o:p></span>

但是$ 11的反向引用沒有返回第11個組,而是返回了第一個附加了1的組,相反,我得到了以下結果:

<span style="font-size:11.0pt"><a href="tel:675-441-3144">675-441-3144</a>>1o:p></o:p></span>

有人可以告訴我如何訪問我的模式的第11組嗎?

謝謝。

簡短答案

訪問替換中比賽的第十一組的方式是使用$11

說明:

相應的Javadoc *所述:

替換字符串可能包含對先前匹配過程中捕獲的子序列的引用: ${name}$g每次出現都將被分別評估相應group(name)group(g)的結果替換。 對於$g ,在之后的第一個數字$始終被視為該組參考的一部分。 如果后續數字將構成合法的組引用,則將其合並到g

因此,一般來講,只要至少有11個組,則"$11"將評估為group(11) 但是,如果您沒有至少11個組,則"$11"將計算為group(1) + "1"

* 此引用來自Matcher#appendReplacement(StringBuffer,String) ,這是來自String#replaceAll(String,String)的相關引用鏈的所在。


實際答案

您的正則表達式不會執行您認為的操作。

第1部分

問題

讓我們將正則表達式分為三個頂級組。 它們分別是組1、2和11。

  • 第一組:
    (>[^<]*?)
  • 第2組:
    ((\\+?\\d{1,4}[ \\t\\f\\-\\.](\\d[ \\t\\f\\-\\.])?)?(\\(\\d{1,4}([\\s-]\\d{1,4})?\\)[\\.\\- \\t\\f])?((\\d{2,6}[\\.\\- \\t\\f])+\\d{2,6})|(\\d{6,16})([;,\\.]{1,3}\\d{3,}#?)?)
  • 第11組:
    ([^<]*<)

第2組是您的正則表達式的主體,它由兩個選項的頂級交替組成。 這兩個選項分別由3-8組和9-10組組成。

  • 第一種選擇:
    ((\\+?\\d{1,4}[ \\t\\f\\-\\.](\\d[ \\t\\f\\-\\.])?)?(\\(\\d{1,4}([\\s-]\\d{1,4})?\\)[\\.\\- \\t\\f])?((\\d{2,6}[\\.\\- \\t\\f])+\\d{2,6})
  • 第二種選擇:
    (\\d{6,16})([;,\\.]{1,3}\\d{3,}#?)?)

現在,給定text字符串,這是怎么回事:

  1. 組1執行。 它與第一個">"相匹配。
  2. 第2組執行。 它按順序評估其交替的選項。
    1. 執行第2組交替的第一個選項。 匹配"675-441-3144"
    2. 第2組的交替在其選項之一匹配時成功短路。
      • 現在,第2組整體等於匹配的選項,即"675-441-3144"
      • 現在將光標定位在緊跟在"675-441-3144" ";;;78888464#"之前的";;;78888464#"
  3. 第11組執行。 它通過下一個"<"匹配所有內容;下一個"<"是所有";;;78888464#<"

因此,您希望放在第2組中的某些內容實際上是在第11組中。

解決方案

請同時執行以下兩項操作:

  • 將第2組的內容轉換為

     option1|option2 

     option1(option2)?|option2 
  • 將替換模式中的$11更改$11 $12

這會使貪婪地匹配一個或兩個選項,而不是只有一個選項。 替換模式的修改是因為我們添加了一個組。

第2部分

問題

現在,我們已經修改了正則表達式,原來的“選項2”不再有意義。 給定我們新的模式模板option1(option2)?|option2 ,第2組將不可能匹配"675-441-3144;;;78888464#" 這是因為我們原來的“選項1”將匹配所有"675-441-3144" ,然后停止。 然后,我們原始的“選項2”將嘗試匹配";;;78888464#" ,但將無法匹配,因為它以6-10位數字的強制捕獲組開頭: (\\d{6,16}) ,但";;;78888464#"以分號開頭。

解決方案

將原始“選項2”的內容轉換為

(\d{6,16})([;,\.]{1,3}\d{3,}#?)?

([;,\.]{1,3}\d{3,}#?)?

第三部分

問題

我們還有最后一個問題要解決。 現在,我們原來的“選項2”僅包含一個帶有?? 量詞,它有可能成功匹配零長度子串。 因此,我們的模式模板option1(newoption2)?|newoption2可能會導致長度為零的匹配,這不能滿足匹配電話號碼的預期目的。

解決方案

請執行以下兩個操作:

  • 將新的“選項2”的內容轉換為

    ([;,。] {1,3} \\ d {3,}#?)?

    [;,。] {1,3} \\ d {3,}#?

  • 將替換字符串中的$12更改$12 $10 ,因為現在我們已在兩個位置刪除了一個組。


最終的解決方案

綜上所述,我們最終的解決方案如下。

搜索正則表達式:

(>[^<]*?)((\+?\d{1,4}[ \t\f\-\.](\d[ \t\f\-\.])?)?(\(\d{1,4}([\s-]\d{1,4})?\)[\.\- \t\f])?((\d{2,6}[\.\- \t\f])+\d{2,6})([;,\.]{1,3}\d{3,}#?)?|[;,\.]{1,3}\d{3,}#?)([^<]*<)

替換正則表達式:

$1<a href="tel:$2">$2</a>$10

Java:

final String searchRegex = "(>[^<]*?)((\\+?\\d{1,4}[ \\t\\f\\-\\.](\\d[ \\t\\f\\-\\.])?)?(\\(\\d{1,4}([\\s-]\\d{1,4})?\\)[\\.\\- \\t\\f])?((\\d{2,6}[\\.\\- \\t\\f])+\\d{2,6})([;,\\.]{1,3}\\d{3,}#?)?|[;,\\.]{1,3}\\d{3,}#?)([^<]*<)";
final String replacementRegex = "$1<a href=\"tel:$2\">$2</a>$10";

String text = "<span style=\"font-size:11.0pt\">675-441-3144;;;78888464#<o:p></o:p></span>";
String replacement = text.replaceAll(searchRegex, replacementRegex);

正確性證明

好吧,在嘗試使用replaceall而不成功之后,我不得不自己實現替換方法:

public static String parsePhoneNumbers(String html){
    StringBuilder regex = new StringBuilder(120);
    regex.append("(>[^<]*?)(")
       .append("((\+?\d{1,4}[ \t\f\-\.](\d[ \t\f\-\.])?)?")
       .append("(\(\d{1,4}([\s-]\d{1,4})?\)[\.\- \t\f])?")
       .append("((\d{2,6}[\.\- \t\f])+\d{2,6})|(\d{6,16})")
       .append("([;,\.]{1,3}\d{3,}#?)?)") 
       .append(")+([^<]*<)");

    StringBuilder mutableHtml = new StringBuilder(html.length());
    Pattern pattern = Pattern.compile(regex.toString());
    Matcher matcher = pattern.matcher(html);
    int start = 0;

    while(matcher.find()){
        mutableHtml.append(html.substring(start, matcher.start()));
        mutableHtml.append(matcher.group(1)).append("<a href=\"tel:")
                .append(matcher.group(2)).append("\">").append(matcher.group(2))
                .append("</a>").append(matcher.group(matcher.groupCount()));
        start = matcher.end();

    }
    mutableHtml.append(html.substring(start));
    return mutableHtml.toString();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM