正则表达式:如何用n次出现的子字符串替换字符串

时间:2018-09-06 13:52:53

标签: java regex

作为前提,我有一个HTML文本,其中包含一些<ol>元素。它们具有start属性,但是我使用的框架无法在PDF转换期间解释它们。因此,我尝试应用的技巧是在开始时添加一些不可见的<li>元素。

例如,假设此输入文本:

<ol start="3">
   <li>Element 1</li>
   <li>Element 2</li>
   <li>Element 3</li>
</ol>

我想产生这个结果:

<ol>
   <li style="visibility:hidden"></li>
   <li style="visibility:hidden"></li>
   <li>Element 1</li>
   <li>Element 2</li>
   <li>Element 3</li>
</ol>

因此,将n-1个不可见元素添加到有序列表中。 但是我无法从Java中以通用方式做到这一点。

假设示例中有确切的情况,我可以这样做(使用replace,因此-老实说-没有正则表达式):

htmlString = htmlString.replace("<ol start=\"3\">",
            "<ol><li style=\"visibility:hidden\"></li><li style=\"visibility:hidden\"></li>");

但是,显然,它仅适用于“ start = 3”的情况。我知道我可以使用组来提取“ 3”,但是如何将其用作“变量”以指定字符串<li style=\"visibility:hidden\"></li> n-1次? 感谢您的任何见识。

5 个答案:

答案 0 :(得分:4)

您不能使用正则表达式执行此操作,或者即使您发现某些破解方法也将是次佳的解决方案。

正确的方法是使用HTML解析库(例如Jsoup),然后将<li>标签作为子元素添加到<ol>,特别是使用{{3 }} 方法。 (使用Jsoup,您还可以读取start属性值,以计算要添加的元素的数量)

答案 1 :(得分:3)

自Java 9开始,有一个Matcher.replaceAll method taking a callback function作为参数:

String text = "<ol start=\"3\">\n\t<li>Element 1</li>\n\t<li>Element 2</li>\n\t<li>Element 3</li>\n</ol>";

String result = Pattern
        .compile("<ol start=\"(\\d)\">")
        .matcher(text)
        .replaceAll(m -> "<ol>" + repeat("\n\t<li style=\"visibility:hidden\" />", 
                                         Integer.parseInt(m.group(1))-1));      

对于repeat字符串,您可以从here中获得技巧,也可以使用循环。

public static String repeat(String s, int n) {
    return new String(new char[n]).replace("\0", s);
}

然后,result是:

<ol>
    <li style="visibility:hidden" />
    <li style="visibility:hidden" />
    <li>Element 1</li>
    <li>Element 2</li>
    <li>Element 3</li>
</ol>   

如果您仍然使用Java的较旧版本,则仍然可以按照两个步骤进行匹配和替换。

Matcher m = Pattern.compile("<ol start=\"(\\d)\">").matcher(text);
while (m.find()) {
    int n = Integer.parseInt(m.group(1));
    text = text.replace("<ol start=\"" + n + "\">", 
            "<ol>" + repeat("\n\t<li style=\"visibility:hidden\" />", n-1));
}

由Andreaジーティーオー更新:

我修改了上面的(great)解决方案,其中还包含具有多个属性的<ol>,以使它们的标签不以start结尾(例如,<ol>的字母为{ {1}})。这使用<ol start="4" style="list-style-type: upper-alpha;">处理整个正则表达式。

replaceAll

答案 2 :(得分:1)

使用Jsoup,您可以编写如下内容:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

class JsoupTest {
    public static void main(String[] args){
        String html = "<ol start=\"3\">\n" +
                        "   <li>Element 1</li>\n" +
                        "   <li>Element 2</li>\n" +
                        "   <li>Element 3</li>\n" +
                        "</ol>"
                + "<p>some other html elements</p>"
                + "<ol start=\"5\">\n" +
                        "   <li>Element 1</li>\n" +
                        "   <li>Element 2</li>\n" +
                        "   <li>Element 3</li>\n" +
                        "   <li>Element 4</li>\n" +
                        "   <li>Element 5</li>\n" +
                        "</ol>";

        Document doc = Jsoup.parse(html);
        Elements ols = doc.select("ol");
        for(Element ol :ols){
            int start = Integer.parseInt(ol.attr("start"));
            for(int i=0; i<start-1; i++){
                ol.prependElement("li").attr("style", "visibility:hidden");
            }  
            ol.attributes().remove("start");
            System.out.println(ol);
        }
    }
}

答案 3 :(得分:0)

您可以尝试一下。

$config

答案 4 :(得分:-2)

请使用java Matcher Pattern 来计数 li 标记的出现,并使用 StringBuilder insert 方法以插入不可见的元素。

Matcher m = Pattern.compile("<li>").matcher(s);
        while(m.find()){
           ++count;
        }