【问题标题】:Find matching closing tag in partial html string在部分 html 字符串中查找匹配的结束标记
【发布时间】:2014-11-15 04:52:36
【问题描述】:

我有一个部分 html 字符串,并且给定了开始标记的位置,我希望能够找到匹配的结束标记的位置。我不能使用 html 解析器(至少我认为我不能),因为 html 只是一个 sn-p,而不是完整的 html。我正在查看的部分之前或之后可能存在不匹配的标签。该字符串不包含 dtd、html、head 或 body 标记。

例如:

<div id='something' class='someclass'>
  <h1>Title</h1>
  <div><p>some text</p></div>
  <div>
    <div class='anotherdiv'>
    </div>
    <div class='yetanother'>
    </div>
  </div>
</div>

(位置编号是特定标签开头的
给定位置 0(如果字符串开头),我想获取内容:

  <h1>Title</h1>
  <div><p>some text</p></div>
  <div>
    <div class='anotherdiv'>
    </div>
    <div class='yetanother'>
    </div>
  </div>

给定 39 的位置(第二行 h1 的开头),我想获取内容:

Title

给定 83 的位置(第 4 行 div 的开头),我想获取内容:

    <div class='anotherdiv'>
    </div>
    <div class='yetanother'>
    </div>

到目前为止,我已经尝试了几种方法。首先,我使用strpos 来定位匹配的结束标记,然后查看起点和结束标记之间是否还有另一个开始标记。如果找到,我会寻找下一个匹配的结束标签。很乱。

然后我尝试搜索下一个匹配的开始标签(标签名称前面带有“

最后,我从指定位置的标签开始,并构建了一个开始标签和结束标签的列表(堆栈)——将标签名称推送到开始标签上,并在匹配时弹出标签名称(如果匹配)结束标签,直到堆栈有一项与起始标签匹配。对于每个操作,我都会跟踪位置,因此我以开始位置(开始标记中 > 之后的字符)和结束位置(结束标记

它会忽略不匹配的结束标签。例如,如果有一个开始 p 标记,然后是一个开始 b 标记,那么它会找到结束 /p 标记而没有结束 >b 标签,它会从列表中删除 b 标签。同样,如果它找到一个不在堆栈中的结束标记,它会忽略它。示例:

<p><b>some text</p></b>

&lt;b&gt;&lt;/b&gt; 都被忽略。

这最后一种方法似乎是最好的主意,但我想知道是否有其他人有更好的主意。

我不是在找人来编写代码。我能做到。我正在寻找一个概念/想法来使用。如果我上面的最后一个想法是最好的,我也很想听听。

如果这是个坏主意,或者我在左外野,我也想听听,但如果您能解释为什么 提供更好、更理智的方法来解决问题,我将不胜感激去做吧。

我猜我真正在寻找“现实”检查以确保我没有过度复杂化解决方案。

提前致谢!

仕龙

【问题讨论】:

  • 也许this question 提供了一些见解...
  • 大多数实际的解析器可以配置为从“坏”的 HTML 和/或 XML(它是一个片段)中“恢复”。我先看看你是不是这样。如果这不可能,您可以使用基本的XML Parser,它的使用有点冗长,但并不关心不完整的文档/片段。
  • 这几乎肯定是错误的方法。你真正想做什么?
  • 给定字符串中指向标记开头的位置,我想要位于该标记开头之间的字符串,并且它匹配结束标记。请参阅上面的示例。标签可能是唯一的,也可能不是唯一的,它可能有也可能没有 ID 或任何其他属性。字符串中的位置由用户单击字符串中的特定位置提供。它不是使用 CSS 选择器或其他类似方法定位的。
  • Wrikken -- 例如,如果我剥离了指针之前的所有字符,所以字符串以感兴趣的标签开头,然后我将它传递给解析器,我能可靠地检索标签所包含的字符串?谢谢!

标签: php html parsing search


【解决方案1】:

像这样逐个字符地浏览你的字符串怎么样:

假设字符串名为 s。

int counter = 0;
bool simpleQuote = false;
bool doubleQuote = false;

int lastOpeningBraquetPosition = 0;
int lastClosingBraquetPosition = 0;

for (int i = 0; i < s.size(); i++)
{
  char c = s[i];
  if (c == "\"") 
    doubleQuote = !doubleQuote;
  if (c == "'") 
    simpleQuote = !simpleQuote;

  if ((c == "<") && (!doubleQuote) && (!simpleQuote))
  {
    //the car interest us
    counter++;
    //we save the position of the last "<"
    lastOpeningBraquetPosition = i;
  }

  if ((c == ">") && (!doubleQuote) && (!simpleQuote))
  {
    //the car interest us
    counter--;
    if (counter == 0)
    {
       //TODO : take the interesting part between lastClosingBraquetPosition + 1 and lastOpeningBraquetPosition - 1 with check to ensure to be in the string
       return result;
    }
    //we save the position of the last ">"
    lastClosingBraquetPosition = i;
  }
}

我还没有编译该代码,但原理在这里。

您只能在字符串之外通过字符搜索 来查看字符(TODO :管理 \") 每次找到 时减少它。您保存最后的 位置以提取有趣的部分。

【讨论】:

    【解决方案2】:

    我通过编写伪解析器解决了我的问题。它真的很基础,从指定位置的标签开始。它遍历字符串,识别每个标签和结束标签。它还监视一个自闭合标签(即。)。对于每个开始标签,它将其压入堆栈,对于每个结束标签,如果它与最后一个开始标签匹配,则将其从堆栈中弹出。当它从堆栈中弹出最后一个匹配的标签时,它已经找到了与起始标签匹配的结束标签。

    在工作时,它会跟踪开始标记的结尾和匹配的结束标记的开始。这允许它知道起始标记所包含的字符串的起始位置和结束位置。我添加了一些“智能”来检测和处理不匹配的标签,但总的来说,它就像描述的那样简单。

    我正在使用它来解析网页上的信息,以定位和捕获特定数据。例如,我使用它来将数据表转换为数据库记录,作为将手动输入的 html 表转换为数据库表记录的项目的一部分。它似乎相当快,解析 12 列的超过 10k 条记录并在不到 0.1 秒的时间内将数据插入到表中。

    我选择这种方法而不是使用完整的 html 或 xml 解析器,因为在许多情况下,起始位置是基于另一个元素之后的元素,而不是能够使用 css 选择器。使用涉及特定 html 的 css 选择器来确定起始位置会更加困难,但使用带有已知起始点的 strpos 来跳过一些与所需元素的选择器匹配的 html 很容易。

    【讨论】:

    • 对不起,我应该把它贴出来我的答案。如果我什至知道去哪里看的话,要找到它需要一段时间,因为它是 4 年前的。
    • 是的。如果您发布了答案,那将会很有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    • 2011-05-15
    • 1970-01-01
    • 2022-08-11
    相关资源
    最近更新 更多