【问题标题】:Extract HTML-like tags with PHP使用 PHP 提取类似 HTML 的标签
【发布时间】:2010-12-17 19:56:30
【问题描述】:

我正在尝试从给定的字符串中提取 OUTERMOST 类似 HTML 的特殊标签。这是一个示例字符串:

sample string with <::Class id="some id\" and more">text with possible other tags inside<::/Class> some more text

我需要找到字符串中 <::tag>?我尝试了另一种方法,改用简单的 HTML 标记并使用 DomDocument,但它无法告诉我标记在字符串中的位置。我不能使用外部库,我只是在寻找如何解决这个问题的指针。也许你已经看到了一个完全可以做到这一点的算法——我想看看它。

感谢您的帮助。 附:由于存在嵌套标签,正则表达式解决方案将不起作用。递归正则表达式解决方案也不起作用。我只是在为这种特定情况寻找一个非常简单的解析算法。

【问题讨论】:

    标签: php dom string


    【解决方案1】:

    您在这里谈论的是制作模板。用于解析模板的正则表达式非常慢。相反,您的模板读取/处理引擎应该进行字符串解析。这不是超级容易,但也不是特别难。不过,我的建议是使用另一个模板库,而不是重新发明轮子。

    PHPBB 中有一个开源模板引擎,您可以使用或学习。或者,使用 Smarty 之类的东西。如果性能很重要,请查看Blitz

    【讨论】:

    • 重新发明轮子正是这里的目的。我不会把时间浪费在一些商业项目上,但我想在这里学习一些东西,即使这意味着重新发明轮子。这就是我为自己做事的方式。总是。我宁愿知道事情是如何运作的,也不愿盲目使用。
    • 开源就不是瞎了:)
    • @Marius:是的,您可以随意查看代码,看看它是如何完成的。而且列出的模板引擎都不是商业产品。
    • 我想我必须四处寻找并尝试从代码中理解一些东西;]
    • Blitz 是一个用 C 语言编写的扩展。我以前也见过 - 非常快。 PHPBB 历来被设计师们非常普遍地理解,因为“html-comment”风格在 DreamWeaver 等方面表现得非常好,所以那里有一个优势。
    【解决方案2】:

    strpos + strrpos(哎哟……)

    $str   = 'sample string with <::Class id="some id" and more">text with possible <::Strong>other<::/Strong> tags inside<::/Class> some more text';
    $tag   = '<::';
    $first = strpos($str, $tag);
    $last  = strrpos($str, $tag);
    $rtn   = array();
    $cnt   = 0;
    while ($first<$last)
    {
      if (!$cnt)
      {
        $rtn[] = substr($str, 0, $first);
      }
      ++$cnt;
      $next = strpos($str, $tag, $first+1);
    
      if ($next)
      {
        $pos   = strpos($str, '>', $first);
        $rtn[] = substr($str, $first, $pos-$first+1);
        $rtn[] = substr($str, $pos+1, $next-$pos-1);
        $first = $next;
      }
    }
    

    有了$rtn,你就可以随心所欲了……这段代码还不完美……

    array (
      0 => 'sample string with ',
      1 => '<::Class id="some id" and more">',
      2 => 'text with possible ',
      3 => '<::Strong>',
      4 => 'other',
      5 => '<::/Strong>',
      6 => ' tags inside',
      7 => '<::/Class> some more text',
    )
    

    【讨论】:

    • OP 只是在谈论“最外层”标签。创建一个非常大的模板的整个地图可能会变得很大。只要所有额外的内存都不是问题,这将是一种可行的方法。如果内存是个问题,你也可以在字符串中创建一个 tag=>location 的映射。
    • 感谢您抽出宝贵时间。其实我已经解决了这个问题。查看您的代码,我可以看到我的方法可能完全相同,只是它没有制作地图,它只是替换了第一次出现。
    • 好吧,虽然你已经发布了一种更清洁的解决方案,但不确定它会有多大用处;]
    【解决方案3】:

    所以基本上这就是我想出的。类似 ajreal 的解决方案只是不够干净;] 甚至不确定它是否完美运行,但初步测试成功。

    protected function findFirstControl()
    {
        $pos = strpos($this->mSource, '<::');
    
        if ($pos === false)
            return false;
    
        // get the control name
        $endOfName = false;
        $controlName = '';
        $len = strlen($this->mSource);
        $i = $pos + 3;
    
        while (!$endOfName && $i < $len)
        {
            $char = $this->mSource[$i];
    
            if (($char >= 'a' && $char <= 'z') || ($char >= 'A' && $char <= 'Z'))
                $controlName .= $char;
            else
                $endOfName = true;
    
            $i++;
        }
    
        if ($controlName == '')
            return false;
    
        $posOfEnd = strpos($this->mSource, '<::/' . $controlName, $i);
        $posOfStart = strpos($this->mSource, '<::' . $controlName, $i);
    
        if ($posOfEnd === false)
            return false;
    
        if ($posOfStart > $pos)
        {
            while ($posOfStart > $pos && $posOfEnd !== false && $posOfStart < $posOfEnd)
            {
                $i = $posOfStart + 1;
                $n = $posOfEnd + 1;
                $posOfStart = strpos($this->mSource, '<::' . $controlName, $i);
                $posOfEnd = strpos($this->mSource, '<::/' . $controlName, $n);
            }
        }
    
        if ($posOfEnd !== false)
        {
            $ln = $posOfEnd - $pos + strlen($controlName) + 5;
            return array($pos, $ln, $controlName, substr($this->mSource, $pos, $ln));
        }
        else
            return false;
    }
    

    【讨论】:

      【解决方案4】:

      不是一个可扩展的解决方案,但它有效。

      $startPos = strpos($string, '<::Class');
      $endPos = strrpos($string, '<::/Class>');
      

      注意我使用strrpos 来解决嵌套问题。此外,这将为您提供&lt;::/Class&gt; 的开始位置,而不是结束位置。

      为什么不直接使用常规 XML 和 DOM?或者只是一个现有的模板引擎,比如Smarty

      【讨论】:

      • 它不起作用,因为可能有嵌套标签。我还需要匹配<::anything xml>
      • @Marius:为什么不把它改成 XML 呢?此外,如果您仔细观察,您会发现第二种方法是strrpos,而不是strpos。该函数在字符串中找到 last 匹配项。
      • 同意“只使用常规XML”,然后解析它?
      • 是strrpos没关系,因为可能有两个标签一个接一个。 strrpos 将返回第二个标签的结尾。我尝试过 HTML,但使用 DomDocument 我不仅无法将标签作为对象获取,而且不知道它在字符串中的位置。
      • 正是我的回答试图说明正则表达式的缺陷。即使你想让它工作,它的效率也会非常低,无论如何
      猜你喜欢
      • 2011-03-16
      • 2016-11-11
      • 2017-02-08
      • 1970-01-01
      • 2012-10-20
      • 1970-01-01
      • 2021-07-18
      • 2015-06-10
      • 1970-01-01
      相关资源
      最近更新 更多