【问题标题】:Find nested matching HTML tags in Java在 Java 中查找嵌套的匹配 HTML 标签
【发布时间】:2013-05-29 20:33:09
【问题描述】:

我正在使用 Java 中的有效 HTML 字符串(使用 jsoup 解析,因此所有标签都有结束标签并且格式正确),例如,我需要找到给定标签名称的内容 ,使用以下字符串:

<p> hi! </p>
<p> hi again! </p>
<h1> foo </h1>
<p> bye! </p>

给定标签“p”,我期望的结果是:

1)<p> hi! </p>
2)<p> hi again! </p>
3)<p> bye! </p>

我通过简单地使用 apache.commons.lang 库和方法来完成这个 StringUtils.substringsBetween(String html, String "opentag" , String "endtag") 将返回具有所需结果的字符串数组。但是,当我搜索一个包含完全相同标签的标签时(一个常见的例子是 div),我会得到错误的结果(我明白为什么)

例如,使用 ...

<div>
 <p> hey there </p>
 <div>  
  <div>
   <p> asd </p>
  </div>
 </div>
</div>

我希望得到 3 个结果: 1)

<div>
 <p> hey there </p>
 <div>  
  <div>
   <p> asd </p>
  </div>
 </div>
</div>

2)

<div>  
 <div>
  <p> asd </p>
 </div>
</div>

3)

<div>
 <p> asd </p>
</div>

但是我得到了一个(我知道它是因为标签的出现如何出现在字符串中)我只是不知道如何解决它。我已经为此苦苦挣扎了 2 周,我尝试过使用正则表达式但完全没有成功,我还尝试将 html 字符串拆分为行数组,但也失败了。

您将如何解决这个问题?我已经知道有很多库可以使用 jsoup 的 getAllElementsByTag(tagName) 等方法为您执行此操作,但我想自己做。任何提示表示赞赏!

【问题讨论】:

  • 如果 (1) 你知道 HTML 是有效的并且 (2) 有更理智的(阅读:DOM)API 来处理 HTML,那么查看单独的开始和结束标签有什么意义呢?要正确执行此操作,您将有效地重新实现 HTML 解析器的某些部分。
  • 多么令人耳目一新!一个不要求使用正则表达式的 HTML 解析问题:p
  • @Gus 在问题末尾禁止使用库如何改变答案?为什么不通读就回答问题?
  • @Gus 很公平,下次我会记住这一点,对于一个如此关心大脑周期的人来说,回答一个他们甚至没有读完的问题仍然很奇怪。

标签: java html tags jsoup


【解决方案1】:

您将需要大量使用标记化和递归来解决此问题。本质上,每次打开一个新标签(例如,&lt;div&gt;)时,您都会再次启动您的处理过程。

考虑以下内容:

ArrayList<String> elements = new ArrayList<String>();
Scanner scanner = new Scanner(html);

public String populateDivContents(String buildingString) {

    while(scanner.hasNext()) {

        //Get the next token
        String next = scanner.next();

        //If it's a <div>, call recursively
        if(next.equalsIgnoreCase("<div>")) {
            buildingString = buildingString + populateDivContents(next);
        }

        //If we've hit a closing tag, add our built String to the elements
        else if(next.equalsIgnoreCase("</div>") {
            buildingString = buildingString + next;
            elements.add(buildingString);
            return buildingString;
        }

        //Otherwise, simply add the text to our String and keep going
        else {
            buildingString = buildingString + next;
        }
    }
}

这是一个非常粗略的草图并且存在一些问题,特别是如果您的标签没有用新行或空格与其内容分隔(就像在您的示例中那样)。正如您所说,它还假设 HTML 格式正确。但这足以让我们理解这个想法。声明的ArrayList&lt;String&gt; 将包含所有&lt;div&gt; 标记及其内容。

【讨论】:

  • 我会尝试使用这个想法,非常感谢您的时间和洞察力!
  • @user2447992 :) 祝你好运!
【解决方案2】:

对此的标准方法是使用堆栈。即,当您遇到开始标签时,您会转储到堆栈中,并且每当遇到结束标签时,您都会弹出最顶层的项目。如果字符串确实格式正确,则所有结束标签都应该弹出一个匹配的开始标签。从那里开始,弄清楚如何获取内部对的内容应该是小菜一碟。

【讨论】:

  • 可能会将您找到它的位置转储到堆栈中,然后使用您弹出的内容和当前位置作为子字符串。困难的部分是识别所有的合法变体,例如
  • @Gus 是的,例如。您也可以只记录最后一个开始标签中的所有内容,并在找到 时将其声明为您的结果。但是您对变体是绝对正确的。使用命名元素肯定会变得更难。
【解决方案3】:

TagSoupJsoup 将是您正在寻找的人:)

【讨论】:

    猜你喜欢
    相关资源
    最近更新 更多
    热门标签